【问题标题】:Rust references don't implement Eq/Hash? How to use them as hash map key?Rust 引用不实现 Eq/Hash?如何将它们用作哈希映射键?
【发布时间】:2021-09-24 01:31:09
【问题描述】:

我想构建一个哈希图,其中键是引用。我希望这些引用的相等意味着引用相等,即两个引用借用同一个对象。

use std::collections::hash_map::HashMap;

struct SomeKey();
struct SomeValue();

fn main() {
    let m = HashMap::<&SomeKey, SomeValue>::new();
    
    let t = SomeKey();
    m.get(&t);
}

不幸的是,这失败了,编译器告诉我&amp;SomeKey 没有实现Hash/Eq

error[E0599]: the method `get` exists for struct `HashMap<&SomeKey, SomeValue>`, but its trait bounds were not satisfied
  --> src/main.rs:10:7
   |
10 |     m.get(&t);
   |       ^^^ method cannot be called on `HashMap<&SomeKey, SomeValue>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&SomeKey: Eq`
           `&SomeKey: Hash`

(Playground)

我注意到,如果我为SomeKey 实现Eq+Hash,那么它可以工作,但是这可能会使用底层对象相等,这不是我想要的。

有没有一种方法可以基于指针相等性将引用用作哈希映射键?

【问题讨论】:

标签: rust object-identity


【解决方案1】:

您可以使用by_address crate。它包装任何指针/引用类型以按地址而不是按内容比较对象。

use std::collections::hash_map::HashMap;

use by_address::ByAddress;

struct SomeKey();
struct SomeValue();

fn main() {
    let mut m = HashMap::<ByAddress<&SomeKey>, SomeValue>::new();

    let t1 = SomeKey();
    let t2 = SomeKey();

    m.insert(ByAddress(&t1), SomeValue());
    assert!(m.get(&ByAddress(&t1)).is_some());
    assert!(m.get(&ByAddress(&t2)).is_none());
}

【讨论】:

    【解决方案2】:

    这可以通过在引用上实现HashEq 然后使用std::ptr 中的函数来执行操作来处理。将引用转换为usize,然后对它们进行操作也可以。您只需确保在 impls 中取消引用一次,因为 &amp;Self 的类型为 &amp;&amp;SomeKey

    use std::collections::HashMap;
    
    struct SomeKey();
    struct SomeValue();
    impl<'a> PartialEq for &'a SomeKey{
        fn eq(&self, other:&Self) -> bool{
            std::ptr::eq(*self, *other)
        }
    }
    impl<'a> Eq for &'a SomeKey{}
    use std::hash::Hash;
    use std::hash::Hasher;
    impl<'a> Hash for &'a SomeKey {
        fn hash<H: Hasher>(&self, state: &mut H) {
            std::ptr::hash(*self, state)
        }
    }
    
    
    fn main() {
        let m = HashMap::<&SomeKey, SomeValue>::new();
        
        let t = SomeKey();
        m.get(&&t);
    }
    

    Playground.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-28
      • 2023-01-18
      • 2012-02-11
      • 1970-01-01
      • 2013-11-22
      • 2017-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多