本文简要介绍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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。