【问题标题】:Is there any way to look up in HashSet by only the value the type is hashed on?有什么方法可以仅通过散列类型的值在 HashSet 中查找?
【发布时间】:2017-07-29 03:27:07
【问题描述】:

我有一个结构,除其他数据外,它还有一个独特的id

struct Foo {
    id: u32,
    other_data: u32,
}

我想使用id 作为键并将其保留在结构中:

use std::collections::HashSet;
use std::hash::{Hash, Hasher};
impl PartialEq for Foo {
    fn eq(&self, other: &Foo) -> bool {
        self.id == other.id
    }
}
impl Eq for Foo {}
impl Hash for Foo {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

这行得通:

pub fn bar() {
    let mut baz: HashSet<Foo> = HashSet::new();
    baz.insert(Foo {
        id: 1,
        other_data: 2,
    });
    let other_data = baz.get(&Foo {
        id: 1,
        other_data: 0,
    }).unwrap()
        .other_data;
    println!("other_data: {}", other_data);
}

有什么办法可以写baz.get(1).unwrap().other_data;而不是baz.get(&amp;Foo { id: 1, other_data: 0 }).unwrap().other_data;

另一种可能是HashMap,其中密钥包含在struct 中。但是,我不能在结构中使用 id,并且不能在 key 中使用重复的 id

【问题讨论】:

  • 我不太了解 Rust。在基于引用的语言(如 Java)中,当出现此问题时,您将使用盒装键(无论您是否喜欢在 Java 中都可以得到),并在 HashMap 输入键字段中引用它,而在对应的值对象。在像 C 或 Ada 这样允许引用/指向未装箱类型的语言中,您可以将值存储在任一位置,并将对它的引用存储在另一个位置。 Afaik Rust 集合肯定允许第一个解决方案,也许还有第二个解决方案。
  • 也许使用枚举 {Key(u32), KeyAndData(u32,u32)} 并实现 hash/eq 以便您可以按键查询并获取 keyanddata?这样您就不必构建Foo 的尾部。无论如何,你没有说为什么你真的需要这个,因为你的玩具示例已经可以在没有修改的情况下工作。
  • @Gene 您的第一点有效(请参阅How can I keep a reference to a key after it has been inserted into a HashMap?),但您无法引用密钥,因为该解决方案不是内存安全的(Rust 会告诉您)-键的地址可以随着添加/删除的东西而改变,所以你会有一个悬空指针。 Rust 实际上也不够聪明,无法判断盒装的情况也很好。请参阅What's the idiomatic way to make a lookup table which uses field of the item as the key? 了解更多信息。
  • 谢谢。感谢您的回复。我只阅读了 Rust 的文档,但它似乎有很多我长期以来一直希望用一种语言实现的想法。

标签: hashmap rust hashset


【解决方案1】:

当您查看HashSet::get 的签名时,您会发现它比您预期的要复杂一些:

fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> 
where
    T: Borrow<Q>,
    Q: Hash + Eq, 

这样做正是为了解决您的问题get 接受对可以从集合中的类型 (T: Borrow&lt;Q&gt;) 中借用的任何类型 (&amp;Q) 的引用。 T 应该读作“我的类型”,Q 应该读作“查询类型”。

因此,您需要为您的类型实现Borrow

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
    }
}

fn main() {
    let mut baz = HashSet::new();
    baz.insert(Foo {
        id: 1,
        other_data: 2,
    });

    let other_data = baz.get(&1).unwrap().other_data;
    println!("other_data: {}", other_data);
}

另见:

【讨论】:

    猜你喜欢
    • 2017-11-02
    • 1970-01-01
    • 2015-12-29
    • 2022-01-18
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    相关资源
    最近更新 更多