Borrow和BorrowMut trait 是Rust標(biāo)準(zhǔn)庫std:borrow 模塊中用于處理借用數(shù)據(jù)的trait,通過實(shí)現(xiàn)Borrow 和BorrowMut trait可以讓一個(gè)類型被借用成不同的引用。
1、AsRef VS Borrow 轉(zhuǎn)換與借用
Borrow trait的定義如下:
pub trait Borrow
對(duì)比一下 AsRef:
pub trait AsRef
可以看出AsRef的定義和Borrow的定義十分相像,那么既然有了AsRef trait,為啥還有Borrow trait的存在呢?
AsRef trait用來表示**類型轉(zhuǎn)換, **Borrow trait用來表示 借用數(shù)據(jù), 在Rust中,為不同的語義不同的使用情況提供不同的類型表示是很常見的。
一個(gè)類型通過實(shí)現(xiàn) Borrow trait,在 borrow()方法中提供對(duì) T 的引用/借用,表達(dá)的語義是可以作為某個(gè)類型 T被借用,而非轉(zhuǎn)換。一個(gè)類型可以自由地借用為幾個(gè)不同的類型,也可以用可變的方式借用。
Borrow trait這類特性存在的意義旨在于解決特定領(lǐng)域的問題,例如在 Hashset,HashMap,BTreeSet,BtreeMap 中使用 &str 查詢 String 類型的鍵。
所以 Borrow 和 AsRef 如何選呢?
- 當(dāng)你想把不同類型的借用進(jìn)行統(tǒng)一抽象,或者當(dāng)你要建立一個(gè)數(shù)據(jù)結(jié)構(gòu),以同等方式處理自擁有值(ownered)和借用值(borrowed)時(shí),例如散列(hash)和比較(compare)時(shí),選擇Borrow。
- 當(dāng)你想把某個(gè)類型直接轉(zhuǎn)換為引用,并且你正在編寫通用代碼時(shí),選擇AsRef。比較簡(jiǎn)單的情況。
2、Borrow 和 BorrowMut 實(shí)現(xiàn)借用數(shù)據(jù)
BorrowMut trait的定義如下:
pub trait BorrowMut
BorrowMut trait類似于Borrow,用于可變借用。BorrowMut trait繼承自Borrowed trait。因此,一個(gè)類型如果實(shí)現(xiàn)了BorrowMut trait,則它也實(shí)現(xiàn)了Borrowed trait。
從Borrow trait的文檔中看,它對(duì)類型實(shí)現(xiàn)借用數(shù)據(jù)強(qiáng)加了更多的限制:
如果一個(gè)類型U實(shí)現(xiàn)了Borrow,在為U實(shí)現(xiàn)額外的trait(特別是實(shí)現(xiàn)Eq, Ord, Hash)的時(shí)候應(yīng)該實(shí)現(xiàn)與T相同的行為。
這句話可以從例子理解,例如String實(shí)現(xiàn)了Borrow,那么在為String實(shí)現(xiàn)Eq, Ord, Hash等trait時(shí),實(shí)現(xiàn)的行為應(yīng)該與str實(shí)現(xiàn)相同。
Borrow trait的文檔中給了一個(gè)HashMap的例子,HashMap利用了String實(shí)現(xiàn)Borrow時(shí),String和str對(duì)Eq, Hash的實(shí)現(xiàn)是相同的這一點(diǎn),可以讓我們可以使用&str作為Key來訪問一個(gè)HashMap。HashMap的定義如下:
use std::borrow::Borrow;
use std:#:Hash;
pub struct HashMap
可以看到get方法的參數(shù)k類型是&Q,而不是&K。Q的trait bound是Hash + Eq + ?Sized,而K的trait bound是Borrow。這里K實(shí)現(xiàn)了Borrow, Hash, Eq等作為額外的trait,Borrow trait約定的限制是K和Q對(duì)這些額外trait的實(shí)現(xiàn)行為是相同的。
上面例子說明在寫通用的代碼時(shí),如果依賴了Hash, Eq等這些額外的trait的相同的行為,會(huì)使用Borrow trait。這些trait作為trait bounds出現(xiàn)。
再看一個(gè)例子:
// 這個(gè)結(jié)構(gòu)體能不能作為 HashMap 的 key?
pub struct CaseInsensitiveString(String);
// 它實(shí)現(xiàn) Eq 沒有問題
impl PartialEq for CaseInsensitiveString {
fn eq(&self, other: &Self) -> bool {
// 但這里比較是要求忽略了 ascii 大小寫
self.0.eq_ignore_ascii_case(&other.0)
}
}
impl Eq for CaseInsensitiveString { }
// 實(shí)現(xiàn) Hash 沒有問題
// 但因?yàn)?eq 忽略大小寫,那么 hash 計(jì)算也必須忽略大小寫
impl Hash for CaseInsensitiveString {
fn hash
但是 CaseInsensitiveString 可以實(shí)現(xiàn) Borrow嗎?
很顯然,CaseInsensitiveString 和 str 對(duì) Hash 的實(shí)現(xiàn)不同,str 是不會(huì)忽略大小寫的。因此,CaseInsensitiveString不能實(shí)現(xiàn)Borrow,所以 CaseInsensitiveString 不能作為 HashMap 的 key,編譯器就可以通過 Borrow trait 來識(shí)別這種情況了。但是 CaseInsensitiveString 完全可以實(shí)現(xiàn) AsRef 。這就是 Borrow 和 AsRef 的區(qū)別,Borrow 更加嚴(yán)格一些,并且表示的語義和 AsRef 完全不同。
3、Borrow和BorrowMut的blanket implement
對(duì)于Borrow和BorrowMut,Rust為泛型T和&T自動(dòng)實(shí)現(xiàn)了這兩個(gè)trait。
#[stable(feature = "rust1", since = "1.0.0")]
impl
4. 為什么Borrow和BorrowMut被定義為泛型trait
被定義為泛型trait,這樣就可以讓同一個(gè)類型同時(shí)有多個(gè)Borrow和BorrowMut trait的實(shí)現(xiàn), 這樣這個(gè)類型就可以同時(shí)讓多個(gè)不同的引用類型作為它的借用。
例2:
fn main() {
let s = String::from("hello");
let s1: &str = s.borrow();
let s2: &String = s.borrow();
println!("s1: {s1:p}, s2: {s2:p}"); // s1: 0x7ff58ec05bc0, s2: 0x7ffee9169fe0
}
例2中引用類型&str和&String都可以作為String類型的借用。即通過實(shí)現(xiàn)Borrow trait可以讓一個(gè)類型被借用成不同的引用。
總結(jié)
Borrow trait是用來表示 借用數(shù)據(jù) ,一個(gè)類型通過實(shí)現(xiàn) Borrow trait,在 borrow()方法中提供對(duì) T 的引用/借用,表達(dá)的語義是可以作為某個(gè)類型 T被借用,而非轉(zhuǎn)換。一個(gè)類型可以自由地借用為幾個(gè)不同的類型,也可以用可變的方式借用。
1 如果一個(gè)類型實(shí)現(xiàn)了Borrow,那么這個(gè)類型的borrow方法可以從其借用一個(gè)&T。
2 可以將 Borrow 和 BorrowMut視作 AsRef和 AsMut的嚴(yán)格版本,其返回的引用 &T 具有與 Self 相同的 Eq,Hash 和 Ord 的實(shí)現(xiàn)。
注:理解Borrow trait這類特性存在的意義,有助于我們揭開 HashSet,HashMap,BTreeSet 和 BTreeMap 中某些方法的實(shí)現(xiàn)的神秘面紗。但是在實(shí)際應(yīng)用中,幾乎沒有什么地方需要我們?nèi)?shí)現(xiàn)這樣的特性,因?yàn)樵匐y找到一個(gè)需要我們對(duì)一個(gè)值再創(chuàng)造一個(gè)“借用”版本的類型的場(chǎng)景了。對(duì)于某種類型 T ,&T 就能解決 99.9% 的問題了,且 T: Borrow 已經(jīng)被一攬子泛型實(shí)現(xiàn)對(duì) T 實(shí)現(xiàn)了,所以我們無需手動(dòng)實(shí)現(xiàn)它,也無需去實(shí)現(xiàn)某種的對(duì) U 有 T: Borrow 了。
-
Hash算法
+關(guān)注
關(guān)注
0文章
43瀏覽量
7657 -
rust語言
+關(guān)注
關(guān)注
0文章
57瀏覽量
3278
發(fā)布評(píng)論請(qǐng)先 登錄
詳解Rust的泛型
Rust中的From和Into trait的基礎(chǔ)使用方法和進(jìn)階用法
Java泛型的背景和作用
labview連接mongdb問題,找到不.NET類中的泛型類
java 泛型編程
聊聊java泛型實(shí)現(xiàn)的原理與好處
Java泛型的工作原理和案例
在trait中使用 `async fn`
rust語言基礎(chǔ)學(xué)習(xí): 智能指針之Cow
rust語言基礎(chǔ)學(xué)習(xí): Default trait
實(shí)現(xiàn)Rust Trait類型 那么該類型的引用也實(shí)現(xiàn)了trait嗎?
鴻蒙語言TypeScript學(xué)習(xí)第18天:【泛型】
為什么Borrow和BorrowMut被定義為泛型trait呢?
評(píng)論