当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


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