當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Rust Borrow用法及代碼示例


本文簡要介紹rust語言中 Trait std::borrow::Borrow 的用法。

用法

pub trait Borrow<Borrowed> where    Borrowed: ?Sized, {
    fn borrow(&self) -> &Borrowed;
}

借用數據的特征。

在 Rust 中,為不同的用例提供不同類型的表示是很常見的。例如,可以通過指針類型(例如 Box<T> Rc<T> )根據特定用途具體選擇值的存儲位置和管理。除了可以與任何類型一起使用的這些通用包裝器之外,某些類型還提供了可選方麵,這些方麵提供了潛在的昂貴函數。這種類型的一個示例是 String ,它增加了將字符串擴展至基本 str 的能力。這需要為簡單的、不可變的字符串保留不必要的附加信息。

這些類型通過引用該數據的類型提供對基礎數據的訪問。據說他們是'borrowed as'那種類型。例如, Box<T> 可以作為 T 借用,而 String 可以作為 str 借用。

類型表示它們可以通過實現 Borrow<T> 被借用為某種類型 T,在 trait 的 borrow 方法中提供對 T 的引用。一個類型可以作為幾種不同的類型自由借用。如果它希望可變地借用作為類型 - 允許修改基礎數據,它可以另外實現 BorrowMut<T>

此外,在為其他特征提供實現時,需要考慮它們是否應該與底層類型的行為相同,因為它們是作為底層類型的表示的結果。當通用代碼依賴於這些附加特征實現的相同行為時,它通常使用Borrow<T>。這些特征可能會作為額外的特征界限出現。

特別是 EqOrdHash 對於借用值和自有值必須等效: x.borrow() == y.borrow() 應給出與 x == y 相同的結果。

如果泛型代碼隻需要適用於所有可以提供對相關類型 T 的引用的類型,那麽使用 AsRef<T> 通常會更好,因為更多類型可以安全地實現它。

例子

作為一個數據集合, HashMap<K, V> 擁有鍵和值。如果鍵的實際數據被包裝在某種管理類型中,那麽仍然應該可以使用對鍵數據的引用來搜索值。例如,如果鍵是一個字符串,那麽它很可能與哈希映射一起存儲為 String ,而應該可以使用 &str 進行搜索。因此,insert 需要對 String 進行操作,而 get 需要能夠使用 &str

稍微簡化一下,HashMap<K, V> 的相關部分如下所示:

use std::borrow::Borrow;
use std::hash::Hash;

pub struct HashMap<K, V> {
    // fields omitted
}

impl<K, V> HashMap<K, V> {
    pub fn insert(&self, key: K, value: V) -> Option<V>
    where K: Hash + Eq
    {
        // ...
    }

    pub fn get<Q>(&self, k: &Q) -> Option<&V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq + ?Sized
    {
        // ...
    }
}

整個哈希映射在鍵類型 K 上是通用的。因為這些鍵與哈希映射一起存儲,所以這種類型必須擁有鍵的數據。插入鍵值對時,映射被賦予這樣的 K 並且需要找到正確的哈希桶並根據該 K 檢查鍵是否已經存在。因此它需要 K: Hash + Eq

但是,在映射中搜索值時,必須提供對 K 的引用作為要搜索的鍵,這將需要始終創建此類擁有的值。對於字符串鍵,這意味著需要創建一個 String 值,僅用於搜索隻有 str 可用的情況。

相反,get 方法對基礎 key 數據的類型是通用的,在上麵的方法簽名中稱為 Q。它指出 K 通過要求 K: Borrow<Q> 作為 Q 借用。通過另外要求 Q: Hash + Eq ,它表示要求 KQ 具有產生相同結果的 HashEq 特征的實現。

get 的實現特別依賴於 Hash 的相同實現,通過對 Q 值調用 Hash::hash 來確定 key 的哈希桶,即使它是根據 K 計算的哈希值插入 key 的值。

因此,如果包裝 Q 值的 K 產生的散列值與 Q 不同,散列映射就會中斷。例如,假設您有一個類型,它包裝一個字符串,但比較 ASCII 字母忽略它們的大小寫:

pub struct CaseInsensitiveString(String);

impl PartialEq for CaseInsensitiveString {
    fn eq(&self, other: &Self) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }
}

impl Eq for CaseInsensitiveString { }

因為兩個相等的值需要產生相同的哈希值,所以Hash的實現也需要忽略ASCII大小寫:

impl Hash for CaseInsensitiveString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for c in self.0.as_bytes() {
            c.to_ascii_lowercase().hash(state)
        }
    }
}

CaseInsensitiveString 可以實現 Borrow<str> 嗎?它當然可以通過其包含的自有字符串提供對字符串切片的引用。但是因為它的 Hash 實現不同,它的行為與 str 不同,因此實際上不能實現 Borrow<str> 。如果它想允許其他人訪問底層的 str ,它可以通過不帶任何額外要求的 AsRef<str> 來實現。

相關用法


注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Trait std::borrow::Borrow。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。