本文簡要介紹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>
。這些特征可能會作為額外的特征界限出現。
特別是 Eq
、 Ord
和 Hash
對於借用值和自有值必須等效: 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
,它表示要求 K
和 Q
具有產生相同結果的 Hash
和 Eq
特征的實現。
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 Borrow用法及代碼示例
- Rust BorrowMut.borrow_mut用法及代碼示例
- Rust Borrow.borrow用法及代碼示例
- Rust Box.downcast用法及代碼示例
- Rust Bound.cloned用法及代碼示例
- Rust Box.try_new_uninit_in用法及代碼示例
- Rust Box.new_in用法及代碼示例
- Rust Box.new_zeroed_in用法及代碼示例
- Rust Box.try_new_zeroed_slice用法及代碼示例
- Rust Box.try_new_in用法及代碼示例
- Rust Box.try_new_zeroed用法及代碼示例
- Rust Box.into_raw用法及代碼示例
- Rust Box.from_raw_in用法及代碼示例
- Rust Box.try_new_zeroed_in用法及代碼示例
- Rust Box.new_zeroed_slice用法及代碼示例
- Rust Box.into_raw_with_allocator用法及代碼示例
- Rust Box.new_zeroed_slice_in用法及代碼示例
- Rust Box.try_new_uninit用法及代碼示例
- Rust Box.leak用法及代碼示例
- Rust Bound.map用法及代碼示例
- Rust Box.assume_init用法及代碼示例
- Rust Box.new_uninit用法及代碼示例
- Rust Box.into_inner用法及代碼示例
- Rust Bound用法及代碼示例
- Rust Box.from_raw用法及代碼示例
注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Trait std::borrow::Borrow。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。