我有一个包含唯一 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
}
}
我明白,一旦我将 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;
}
}
这是反模式吗?我应该改用 HashMap
吗?
最佳答案
这对于您当前的数据结构是不可能的。
HashSet
故意不提供改变值的方法。正如您所提到的,在大多数情况下,改变 HashSet
中的值(或 HashMap
中的键)会使散列无效。 API 鼓励正确使用,甚至提到了这一点:
It is a logic error for an item to be modified in such a way that the item's hash, as determined by the
Hash
trait, or its equality, as determined by theEq
trait, changes while it is in the set. This is normally only possible throughCell
,RefCell
, global state, I/O, or unsafe code.
这暗示了一种解决问题的方法,即使用内部可变性:
use std::cell::Cell;
#[derive(Debug, Eq)]
struct Foo {
id: Id,
other_data: Cell<u32>,
}
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);
}
}
这是一件合理的事情,但我不会为此感到兴奋。相反,我会允许将我的类型分解为一个键和一个值,并将其存储在 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;
}
}
关于rust - 如何修改 HashSet 中不属于哈希计算的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55464930/