pub trait Borrow<Borrowed>where
Borrowed: ?Sized,{
// Required method
fn borrow(&self) -> &Borrowed;
}
Expand description
借用数据的 trait。
在 Rust 中,通常为不同的用例提供类型的不同表示形式。
例如,可以通过指针类型 (例如 Box<T>
或 Rc<T>
) 为特定用途适当地选择值的存储位置和管理。
除了这些可以与任何类型一起使用的泛型包装之外,某些类型还提供了可选的构面,从而提供了可能昂贵的功能。
这种类型的一个示例是 String
,它增加了将字符串扩展到基本 str
的功能。
这要求保持简单的不可变字符串不需要的其他信息。
这些类型通过引用该数据的类型来提供对底层数据的访问。据说它们是借来的那种类型的。
例如,可以将 Box<T>
作为 T
借用,而可以将 String
作为 str
借用。
类型表示可以通过实现 Borrow<T>
来借用它们作为某种类型的 T
,并在 trait 的 borrow
方法中对 T
进行引用。一个类型可以自由借用为几种不同的类型。
如果希望将类型可变为借用 - 允许修改底层数据,则可以另外实现 BorrowMut<T>
。
此外,在为额外的 traits 提供实现时,需要考虑它们是否应该作为底层类型的表示而与底层类型的行为相同。
当泛型代码依赖于这些其他 trait 实现的相同行为时,通常会使用 Borrow<T>
。
这些 traits 可能会显示为其他 trait bounds。
特别是对于借用和拥有的值,Eq
,Ord
和 Hash
必须等效: x.borrow() == y.borrow()
的结果应与 x == y
相同。
如果泛型代码只需要为可以为相关类型 T
提供引用的所有类型工作,则通常最好使用 AsRef<T>
,因为更多类型可以安全地实现它。
Examples
作为数据集合,HashMap<K, V>
同时拥有键和值。如果键的实际数据包装在某种管理类型中,则仍然应该可以使用对键数据的引用来搜索值。
例如,如果键是一个字符串,则它很可能与哈希 map 一起存储为 String
,而应该可以使用 &str
进行搜索。
因此,insert
需要在 String
上运行,而 get
需要能够使用 &str
。
略有简化,HashMap<K, V>
的相关部分如下所示:
use std::borrow::Borrow;
use std::hash::Hash;
pub struct HashMap<K, V> {
// 字段省略
}
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
{
// ...
}
}
Run整个哈希 map 在键类型 K
上是泛型的。由于这些键存储在哈希 map 中,因此该类型必须拥有键的数据。
插入键值对时,会给 map 这样的 K
,并且需要找到正确的哈希值存储区,并根据该 K
检查键是否已存在。因此,它需要 K: Hash + Eq
。
但是,在 map 中搜索值时,必须提供 K
的引用作为搜索键,这要求始终创建此类拥有的值。
对于字符串键,这意味着仅在搜索仅 str
可用的情况下,才需要创建 String
值。
相反,get
方法是底层键数据类型的泛型,在上面的方法签名中称为 Q
。它通过要求 K: Borrow<Q>
来声明 K
借用为 Q
。
通过额外要求 Q: Hash + Eq
,它表示要求 K
和 Q
具有 Hash
和 Eq
traits 的实现,它们会产生相同的结果。
get
的实现尤其依赖于 Hash
的相同实现,即通过基于 Q
值计算出的哈希值插入键,通过在 Q
值上调用 Hash::hash
来确定键的哈希存储桶。
结果,如果包装了 Q
值的 K
产生与 Q
不同的哈希,则哈希 map 会中断。例如,假设您有一个包裹字符串但比较 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 { }
Run因为两个相等的值需要产生相同的哈希值,所以 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)
}
}
}
RunCaseInsensitiveString
可以实现 Borrow<str>
吗? 它当然可以通过其包含的拥有的字符串为字符串切片提供引用。
但是由于 Hash
的实现方式不同,所以它的行为与 str
不同,因此,实际上,一定不能实现 Borrow<str>
。
如果它希望允许其他人访问底层 str
,则可以通过 AsRef<str>
来实现,而 AsRef<str>
则没有任何额外的要求。