0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

為什么Borrow和BorrowMut被定義為泛型trait呢?

冬至子 ? 來源:山川與湖水 ? 作者:山川與湖水 ? 2023-05-22 15:57 ? 次閱讀

Borrow和BorrowMut trait 是Rust標準庫std:borrow 模塊中用于處理借用數(shù)據(jù)的trait,通過實現(xiàn)Borrow 和BorrowMut trait可以讓一個類型被借用成不同的引用。

1、AsRef VS Borrow 轉(zhuǎn)換與借用

Borrow trait的定義如下:

pub trait Borrow

對比一下 AsRef:

pub trait AsRef

可以看出AsRef的定義和Borrow的定義十分相像,那么既然有了AsRef trait,為啥還有Borrow trait的存在呢?

AsRef trait用來表示**類型轉(zhuǎn)換, **Borrow trait用來表示 借用數(shù)據(jù), 在Rust中,為不同的語義不同的使用情況提供不同的類型表示是很常見的。

一個類型通過實現(xiàn) Borrow trait,在 borrow()方法中提供對 T 的引用/借用,表達的語義是可以作為某個類型 T被借用,而非轉(zhuǎn)換。一個類型可以自由地借用為幾個不同的類型,也可以用可變的方式借用。

Borrow trait這類特性存在的意義旨在于解決特定領(lǐng)域的問題,例如在 Hashset,HashMap,BTreeSet,BtreeMap 中使用 &str 查詢 String 類型的鍵。

所以 Borrow 和 AsRef 如何選呢?

  • 當你想把不同類型的借用進行統(tǒng)一抽象,或者當你要建立一個數(shù)據(jù)結(jié)構(gòu),以同等方式處理自擁有值(ownered)和借用值(borrowed)時,例如散列(hash)和比較(compare)時,選擇Borrow。
  • 當你想把某個類型直接轉(zhuǎn)換為引用,并且你正在編寫通用代碼時,選擇AsRef。比較簡單的情況。

2、Borrow 和 BorrowMut 實現(xiàn)借用數(shù)據(jù)

BorrowMut trait的定義如下:

pub trait BorrowMut

BorrowMut trait類似于Borrow,用于可變借用。BorrowMut trait繼承自Borrowed trait。因此,一個類型如果實現(xiàn)了BorrowMut trait,則它也實現(xiàn)了Borrowed trait。

從Borrow trait的文檔中看,它對類型實現(xiàn)借用數(shù)據(jù)強加了更多的限制:

如果一個類型U實現(xiàn)了Borrow,在為U實現(xiàn)額外的trait(特別是實現(xiàn)Eq, Ord, Hash)的時候應(yīng)該實現(xiàn)與T相同的行為。

這句話可以從例子理解,例如String實現(xiàn)了Borrow,那么在為String實現(xiàn)Eq, Ord, Hash等trait時,實現(xiàn)的行為應(yīng)該與str實現(xiàn)相同。

Borrow trait的文檔中給了一個HashMap的例子,HashMap利用了String實現(xiàn)Borrow時,String和str對Eq, Hash的實現(xiàn)是相同的這一點,可以讓我們可以使用&str作為Key來訪問一個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實現(xiàn)了Borrow, Hash, Eq等作為額外的trait,Borrow trait約定的限制是K和Q對這些額外trait的實現(xiàn)行為是相同的。

上面例子說明在寫通用的代碼時,如果依賴了Hash, Eq等這些額外的trait的相同的行為,會使用Borrow trait。這些trait作為trait bounds出現(xiàn)。

再看一個例子:

// 這個結(jié)構(gòu)體能不能作為 HashMap 的 key?
pub struct CaseInsensitiveString(String);


// 它實現(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 { }
// 實現(xiàn) Hash 沒有問題
// 但因為 eq 忽略大小寫,那么 hash 計算也必須忽略大小寫
impl Hash for CaseInsensitiveString {
    fn hash

但是 CaseInsensitiveString 可以實現(xiàn) Borrow嗎?

很顯然,CaseInsensitiveString 和 str 對 Hash 的實現(xiàn)不同,str 是不會忽略大小寫的。因此,CaseInsensitiveString不能實現(xiàn)Borrow,所以 CaseInsensitiveString 不能作為 HashMap 的 key,編譯器就可以通過 Borrow trait 來識別這種情況了。但是 CaseInsensitiveString 完全可以實現(xiàn) AsRef 。這就是 Borrow 和 AsRef 的區(qū)別,Borrow 更加嚴格一些,并且表示的語義和 AsRef 完全不同。

3、Borrow和BorrowMut的blanket implement

對于BorrowBorrowMut,Rust為泛型T和&T自動實現(xiàn)了這兩個trait。

#[stable(feature = "rust1", since = "1.0.0")]
impl

4. 為什么Borrow和BorrowMut被定義為泛型trait

被定義為泛型trait,這樣就可以讓同一個類型同時有多個Borrow和BorrowMut trait的實現(xiàn), 這樣這個類型就可以同時讓多個不同的引用類型作為它的借用。

例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類型的借用。即通過實現(xiàn)Borrow trait可以讓一個類型被借用成不同的引用。

總結(jié)

Borrow trait是用來表示 借用數(shù)據(jù) ,一個類型通過實現(xiàn) Borrow trait,在 borrow()方法中提供對 T 的引用/借用,表達的語義是可以作為某個類型 T被借用,而非轉(zhuǎn)換。一個類型可以自由地借用為幾個不同的類型,也可以用可變的方式借用。

1 如果一個類型實現(xiàn)了Borrow,那么這個類型的borrow方法可以從其借用一個&T

2 可以將 BorrowBorrowMut視作 AsRefAsMut的嚴格版本,其返回的引用 &T 具有與 Self 相同的 Eq,Hash 和 Ord 的實現(xiàn)。

注:理解Borrow trait這類特性存在的意義,有助于我們揭開 HashSet,HashMap,BTreeSet 和 BTreeMap 中某些方法的實現(xiàn)的神秘面紗。但是在實際應(yīng)用中,幾乎沒有什么地方需要我們?nèi)崿F(xiàn)這樣的特性,因為再難找到一個需要我們對一個值再創(chuàng)造一個“借用”版本的類型的場景了。對于某種類型 T ,&T 就能解決 99.9% 的問題了,且 T: Borrow 已經(jīng)被一攬子泛型實現(xiàn)對 T 實現(xiàn)了,所以我們無需手動實現(xiàn)它,也無需去實現(xiàn)某種的對 U 有 T: Borrow 了。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Hash算法
    +關(guān)注

    關(guān)注

    0

    文章

    43

    瀏覽量

    7379
  • rust語言
    +關(guān)注

    關(guān)注

    0

    文章

    57

    瀏覽量

    3001
收藏 人收藏

    評論

    相關(guān)推薦

    詳解Rust的

    所有的編程語言都致力于將重復(fù)的任務(wù)簡單化,并為此提供各種各樣的工具。在 Rust 中,(generics)就是這樣一種工具,它是具體類型或其它屬性的抽象替代。在編寫代碼時,我們可以直接描述
    發(fā)表于 11-12 09:08 ?1041次閱讀

    Rust中的From和Into trait的基礎(chǔ)使用方法和進階用法

    、可靠和安全的系統(tǒng)級應(yīng)用的首選語言。 Rust中的From和Into是兩個重要的trait,它們可以幫助我們進行類型轉(zhuǎn)換。From trait允許我們從一個類型轉(zhuǎn)換到另一個類型,而Into trait則允許我們將一個類型轉(zhuǎn)換為另
    的頭像 發(fā)表于 09-20 10:55 ?1713次閱讀

    Java的背景和作用

    )只能存儲Object類型的對象,這使得在使用集合時需要進行強制類型轉(zhuǎn)換,容易出現(xiàn)類型錯誤。 的背景:在Java 5版本之前,Java的類型是靜態(tài)的,在編譯時確定,并且在運行時擦除類型信息。這種情況下,編譯器無法對集合的元素類型進行驗證,因此可能會導(dǎo)致運行時類型錯誤。
    的頭像 發(fā)表于 09-20 14:30 ?1033次閱讀
    Java<b class='flag-5'>泛</b><b class='flag-5'>型</b>的背景和作用

    labview連接mongdb問題,找到不.NET類中的

    有沒有人用labview連接mongodb數(shù)據(jù)庫的?已下載mongodb的c#驅(qū)動,利用labview中的.net控件調(diào)用相關(guān)函數(shù),但是驅(qū)動中有部分函數(shù)在類中, labview能調(diào)用c#中的
    發(fā)表于 04-08 13:38

    冒泡排序法的實現(xiàn)

    冒泡排序法的實現(xiàn),自用筆記!
    發(fā)表于 01-20 07:22

    iOS中關(guān)于的解析

    文章圍繞這五點: 1. 是什么 2. 為什么要用 3. 怎么用 4.
    發(fā)表于 09-25 10:01 ?0次下載

    java 編程

    一。 概念的提出(為什么需要)? 首先,我們看下下面這段簡短的代碼: publicclassGenericTest { publicstaticvoidmain(String[
    發(fā)表于 09-27 11:15 ?0次下載

    聊聊java實現(xiàn)的原理與好處

    摘要: 和C++以模板來實現(xiàn)靜多態(tài)不同,Java基于運行時支持選擇了,兩者的實現(xiàn)原理大相庭徑。C++可以支持基本類型作為模板參數(shù),Java卻只能接受類作為參數(shù);Java可以在
    發(fā)表于 09-27 16:50 ?0次下載

    Java的工作原理和案例

    是Java語言一個非常重要的概念,在Java集合類框架中被廣泛應(yīng)用。在介紹之前先看一個例子。
    的頭像 發(fā)表于 07-01 10:14 ?2621次閱讀

    trait中使用 `async fn`

    trait 中使用?async fn async 工作組很高興地宣布?async fn?現(xiàn)在可以在 nightly 版本的 traits 中使用。在 playground 上有一個完整的工作示例
    的頭像 發(fā)表于 11-23 15:40 ?750次閱讀

    rust語言基礎(chǔ)學(xué)習(xí): 智能指針之Cow

    Rust中與借用數(shù)據(jù)相關(guān)的三個trait: Borrow, BorrowMut和ToOwned。理解了這三個trait之后,再學(xué)習(xí)Rust中能夠?qū)崿F(xiàn)寫時克隆的智能指針Cow
    的頭像 發(fā)表于 05-22 16:13 ?2842次閱讀

    rust語言基礎(chǔ)學(xué)習(xí): Default trait

    Default trait 顧名思義是默認值,即Rust特定類型實現(xiàn) Default 特性時,可以為該類型賦予了可選的默認值。
    的頭像 發(fā)表于 05-22 16:16 ?1220次閱讀

    實現(xiàn)Rust Trait類型 那么該類型的引用也實現(xiàn)了trait嗎?

    如果你在一個類型上實現(xiàn)了一個trait,然后引用了這個類型,那么類型的引用也實現(xiàn)了這個trait嗎?
    的頭像 發(fā)表于 08-28 15:25 ?660次閱讀

    C語言是否支持編程?

    C語言是否支持編程?毫無疑問,答案是不支持。
    的頭像 發(fā)表于 10-16 10:02 ?617次閱讀

    鴻蒙語言TypeScript學(xué)習(xí)第18天:【

    (Generics)是一種編程語言特性,允許在定義函數(shù)、類、接口等時使用占位符來表示類型,而不是具體的類型。
    的頭像 發(fā)表于 04-16 14:56 ?332次閱讀
    鴻蒙語言TypeScript學(xué)習(xí)第18天:【<b class='flag-5'>泛</b><b class='flag-5'>型</b>】