我有一个包含唯一ID的结构,并使用该ID作为其哈希值:
use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
type Id = u32;
#[derive(Debug, Eq)]
struct Foo {
id: Id,
other_data: u32,
}
impl PartialEq for Foo {
fn eq(&self, other: &Foo) -> bool {
self.id == other.id
}
}
impl Hash for Foo {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Borrow<Id> for Foo {
fn borrow(&self) -> &Id {
&self.id
}
}
Run Code Online (Sandbox Code Playgroud)
我了解Foo::id一旦将值放入,就无法修改它的值,HashSet因为那样会改变哈希值。但是,我想修改Foo::other_data。我知道我可以将其从中删除HashSet,进行修改,然后再次插入,但是这样的方法get_mut()会更加干净。有没有办法完成这样的事情:
fn main() {
let mut baz = HashSet::new();
baz.insert(Foo {
id: 1,
other_data: 2,
});
if let Some(x) = baz.get_mut(&1) {
*x = 3;
}
}
Run Code Online (Sandbox Code Playgroud)
这是反模式吗?我应该HashMap改用吗?
当前的数据结构无法做到这一点。
HashSet故意不提供变异值的方法。正如您所暗示的,在大多数情况下,对中的值HashSet(或中的键HashMap)进行突变会使哈希无效。该API鼓励正确使用,甚至提及以下内容:
逻辑上的错误在于,要以某种方式修改某项,以使该项在特征集中的散列(由
Hash特征决定)或相等性(由Eq特征决定)在更改时会发生变化。通常只能通过Cell,,RefCell全局状态,I / O或不安全的代码来实现。
这暗示了使用内部可变性可以解决问题的一种方式:
use std::cell::Cell;
#[derive(Debug, Eq)]
struct Foo {
id: Id,
other_data: Cell<u32>,
}
Run Code Online (Sandbox Code Playgroud)
fn main() {
let mut baz = HashSet::new();
baz.insert(Foo {
id: 1,
other_data: Cell::new(2),
});
if let Some(x) = baz.get(&1) {
x.other_data.set(3);
}
}
Run Code Online (Sandbox Code Playgroud)
这是一件合理的事情,但是我不会为此感到兴奋。相反,我将允许将我的类型分解为一个键和一个值,并将其存储在中HashMap,如前所述。就像是
impl Foo {
// or insert_into_hashmap(self, &mut HashMap<Id, u32>)
fn into_key_value(self) -> (Id, u32) {
(self.id, self.other_data)
}
// Maybe a
//
// fn from_key_value(&'a Id, &'a u32) -> Self
// or
// fn from_hashmap(Id, &HashMap<Id, u32>) -> Self
}
// Maybe a
//
// struct FooRef<'a> { (or FooRefMut?)
// id: &'a Id,
// other_data: &'a u32,
// }
//
// With a
// fn from_key_value(&'a Id, &'a u32) -> Self
// or
// fn from_hashmap(Id, &HashMap<Id, u32>) -> Self
fn main() {
let mut baz = HashMap::new();
let f = Foo {
id: 1,
other_data: 2,
};
let (k, v) = f.into_key_value();
baz.insert(k, v);
// See also HashMap::get_key_value
if let Some(v) = baz.get_mut(&1) {
*v = 3;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
139 次 |
| 最近记录: |