rust - 如何修改 HashSet 中不属于哈希计算的属性?

标签 rust hashset

我有一个包含唯一 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 吗?

Related to this question.

最佳答案

这对于您当前的数据结构是不可能的。

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 the Eq trait, changes while it is in the set. This is normally only possible through Cell, 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/

相关文章:

rust - 在 Rust 中计算 HMAC

struct - 检测新的结构初始化

java - HashSet 添加重复字符串

java - 从 Java 中集合的 HashMap 中找到最常见的值?

java - 我可以以这种方式为(哈希)集实现 hashCode 和 equals 吗?

将迭代器中的项目添加到数据集时的 Java 错误 - 重复单个值

c - 静态链接到C程序的Rust代码是否会因此获得任何有益的安全特性?

c++ - 什么是 C++ 上下文的单态化?

rust - 修改 `iter` 链以使用 `and_then` 等