制作使用项目字段作为关键字的查找表的惯用方式是什么?

col*_*ang 3 rust

我有一个收藏Foo

struct Foo {
    k: String,
    v: String,
}
Run Code Online (Sandbox Code Playgroud)

我想要一个HashMap具有键&foo.k和值的键foo

显然,不Foo通过引入Rc或克隆/复制进行重新设计是不可能的k

fn t1() {
    let foo = Foo { k: "k".to_string(), v: "v".to_string() };
    let mut a: HashMap<&str, Foo> = HashMap::new();
    a.insert(&foo.k, foo); // Error
}
Run Code Online (Sandbox Code Playgroud)

get()HashSetPlayground)滥用似乎有一种解决方法:

use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher, BuildHasher};
use std::collections::hash_map::Entry::*;

struct Foo {
    k: String,
    v: String,
}

impl PartialEq for Foo {
    fn eq(&self, other: &Self) -> bool { self.k == other.k }
}

impl Eq for Foo {}

impl Hash for Foo {
    fn hash<H: Hasher>(&self, h: &mut H) { self.k.hash(h); }
}

impl ::std::borrow::Borrow<str> for Foo {
    fn borrow(&self) -> &str {
        self.k.as_str()
    }
}

fn t2() {
    let foo = Foo { k: "k".to_string(), v: "v".to_string() };
    let mut a: HashSet<Foo> = HashSet::new();
    a.insert(foo);
    let bar = Foo { k: "k".to_string(), v: "v".to_string() };
    let foo = a.get("k").unwrap();
    println!("{}", foo.v);
}
Run Code Online (Sandbox Code Playgroud)

这很繁琐。如果一个Foo具有多个字段并且Foo在不同字段上具有不同的to键,该怎么办?

She*_*ter 5

显然,不Foo通过引入Rc或克隆/复制进行重新设计是不可能的k

没错,不可能有HashMap<&K, V>键指向值的某个部分。

HashMap拥有密钥和值在大矢量概念存储两者。将新值添加到时HashMap,由于哈希冲突,可能需要移动这些现有值,或者可能需要重新分配向量以容纳更多项。这两个操作都会使任何现有密钥的地址无效,使其指向无效的内存。这会破坏Rust的安全保证,因此是不允许的。

阅读为什么我不能在同一结构中存储值和对该值的引用?进行彻底的讨论。

此外,trentcl指出,HashMap::get_mut将允许您获取对key的可变引用,这将允许您在不知道地图的情况下更改key。如文档所述:

以某种方式修改密钥是一种逻辑错误,即,当密钥在映射中时,由哈希特征确定的密钥哈希值或由等价特征确定的相等性会改变。


解决方法包括:

  • 从结构中删除密钥并单独存储。而不是HashMap<&K, V>V 在哪里(K, Data),存储HashMap<K, Data>。您可以返回将对键和值的引用粘合在一起的结构(示例

  • 使用密钥的股权Rc例如

  • 使用Clone或创建重复的密钥Copy

  • HashSet塞巴斯蒂安·雷德尔Sebastian Redl)的建议所增强的那样,照常使用a 。A HashSet<K>实际上只是一个HashMap<K, ()>,因此这可以通过将所有所有权转让给密钥来实现。


Seb*_*edl 5

您可以为集合中存储的项目引入包装类型。

struct FooByK(Foo);
Run Code Online (Sandbox Code Playgroud)

然后实现该结构集所需的各种特征。如果您需要一个由不同成员索引的集合,这允许您选择不同的包装器类型。