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

labview連接mongdb問(wèn)題,找到不.NET類(lèi)中的泛型類(lèi)
java 泛型編程
聊聊java泛型實(shí)現(xiàn)的原理與好處
Java泛型的工作原理和案例
在trait中使用 `async fn`
rust語(yǔ)言基礎(chǔ)學(xué)習(xí): 智能指針之Cow
rust語(yǔ)言基礎(chǔ)學(xué)習(xí): Default trait
實(shí)現(xiàn)Rust Trait類(lèi)型 那么該類(lèi)型的引用也實(shí)現(xiàn)了trait嗎?
鴻蒙語(yǔ)言TypeScript學(xué)習(xí)第18天:【泛型】

評(píng)論