我使用了一个复杂的键来表示HashMap,这样的键由两部分组成,其中一部分是String,如果不 for each 查找分配一个新的String,我就无法理解如何通过HashMap::get方法进行查找.

以下是一些代码:

#[derive(Debug, Eq, Hash, PartialEq)]
struct Complex {
    n: i32,
    s: String,
}

impl Complex {
    fn new<S: Into<String>>(n: i32, s: S) -> Self {
        Complex { n: n, s: s.into() }
    }
}

fn main() {
    let mut m = std::collections::HashMap::<Complex, i32>::new();
    m.insert(Complex::new(42, "foo"), 123);

    // OK, but allocates temporary String
    assert_eq!(123, *m.get(&Complex::new(42, "foo")).unwrap());
}

问题在于最后的断言.它通过了,但需要临时的堆分配,因为如果不构造String,我就无法构造Complex.

为了消除这样的临时分配,Rust提供了Borrow特征,HashMap::get方法利用了Borrow特征.我知道如何使用简单的 keys .例如,Rust Standard Library的PathBuf通过在引擎盖下使用std::mem::transmute实现了Borrow<Path>,但我不知道如何使其适用于我的Complex类型:

#[derive(Debug)]
struct Borrowable {
    // ??? -- What goes here? Perhaps something like:
    n: i32,
    s1: &str, // ??? -- But what would the lifetime be? Or maybe:
    s2: str,  // ??? -- But how would I extend this to a complex type
              //        containing two or more strings?
}

impl Borrowable {
    fn new(n: i32, s: &str) -> &Self {
         // ??? -- What goes here? It must not allocate.
        unimplemented!();
    }
}

impl std::borrow::Borrow<Borrowable> for Complex {
    fn borrow(&self) -> &Borrowable {
        // ??? -- What goes here? How can I transmute a Complex into a
        //        &Borrowable?
        unimplemented!();
    }
}

这似乎是一个常见的用例,我怀疑我错过了关于Borrow的重要信息,但我完全不知所措.

推荐答案

听起来你想要这个.

Cow将接受&strString.

use std::borrow::Cow;

#[derive(Debug, Eq, Hash, PartialEq)]
struct Complex<'a> {
    n: i32,
    s: Cow<'a, str>,
}

impl<'a> Complex<'a> {
    fn new<S: Into<Cow<'a, str>>>(n: i32, s: S) -> Self {
        Complex { n: n, s: s.into() }
    }
}

fn main() {
    let mut m = std::collections::HashMap::<Complex<'_>, i32>::new();
    m.insert(Complex::new(42, "foo"), 123);

    assert_eq!(123, *m.get(&Complex::new(42, "foo")).unwrap());
}

关于生命周期 参数的 comments :

如果您不喜欢lifetime参数,只需要使用&'static strString,那么可以使用Cow<'static, str>并从impl块和 struct 定义中删除其他lifetime参数.

Rust相关问答推荐

为什么我们不能通过指针算法将Rust原始指针指向任意地址?'

Rust kill std::processs::child

当为a Self:IntoIterator设置trait bind `时,获取`a T `不是迭代器"&'"<'>&'

从Rust调用C++虚拟方法即使在成功执行之后也会引发Access违规错误

如何最好地并行化修改同一Rust向量的多个切片的代码?

使用pyo3::Types::PyIterator的无限内存使用量

我如何在Rust中使用传递依赖中的特征?

Tokio_Postgres行上未显示退回特性的生存期,且生命周期 不够长

我可以在不收集或克隆的情况下,将一个带有Item=(key,val)的迭代器拆分成单独的key iter和val iter吗?

Rust面向对象设计模式

提取指向特征函数的原始指针

为什么需要静态生命周期以及在处理 Rust 迭代器时如何缩小它?

为什么这段 Rust 代码会在没有递归或循环的情况下导致堆栈溢出?

按下 Ctrl + C 时优雅地停止命令并退出进程

如何在 Emacs Elisp 中获得类似格式化的 LSP?

使用 traits 时,borrow 的值不会存在足够长的时间

如何获取函数中borrow 的切片的第一部分?

强制特征仅在 Rust 中的给定类型大小上实现

如何为枚举中的单个或多个值返回迭代器

为什么这个 Trait 无效?以及改用什么签名?