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

HiD*_*der 0 hashset rust

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

与这个问题有关。

She*_*ter 7

当前的数据结构无法做到这一点。

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)