【问题标题】:How do I implement `Hash` for an enum with a special case?如何为具有特殊情况的枚举实现“哈希”?
【发布时间】:2021-11-10 04:08:17
【问题描述】:

我有这个枚举:

enum MyEnum {
    Var1,
    Var2(u32),
    Var3(u32, u32),
}

我想实现以下行为:

let mut set = HashSet::new();
set.insert(MyEnum::Var1);
set.insert(MyEnum::Var1);
set.insert(MyEnum::Var2(1));
set.insert(MyEnum::Var2(2));
set.insert(MyEnum::Var3(1, 1));
set.insert(MyEnum::Var3(1, 1));
set.insert(MyEnum::Var3(2, 1));
set.insert(MyEnum::Var3(2, 2));

println!("set = {:?}", set);
// set = {Var1, Var2(1), Var2(2), Var3(1, 1), Var3(2, 1)}

也就是说,我想更改变体Var3Hash 行为特别,使其仅依赖于其第一个u32

重要的是,我不想详尽地match 每个枚举变体,因为我想保留所有其他枚举变体的默认行为。因此,据我所知,these answers 不足以满足我的需求。

我想出的解决方案是这样的:

#[derive(Debug, Eq)]
enum MyEnum {
    Var1,
    Var2(u32),
    Var3(u32, u32),
}

impl PartialEq for MyEnum {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (MyEnum::Var3(a, _), MyEnum::Var3(b, _)) => a == b,
            _ => format!("{:?}", self) == format!("{:?}", other),
        }
    }
}

impl Hash for MyEnum {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            MyEnum::Var3(a, _) => a.hash(state),
            _ => format!("{:?}", self).hash(state)
        }
    }
}

...我正在寻找反馈/更好的解决方案。

【问题讨论】:

    标签: rust


    【解决方案1】:

    answer by @kmdreko 展示了如何使用第三方 crate 来实现 Hash 特征。在大多数情况下,这是首选方式,因为它需要的代码更少,而且您不必手动确保不违反 Hash-Eq contract

    为了完整起见,您可以在没有第三方 crate 的情况下手动实现它:

    impl Hash for MyEnum {
        fn hash<H: Hasher>(&self, state: &mut H) {
            match self {
                MyEnum::Var1 => {
                    state.write_u8(1); // use any distinct number as an enum variant identifier
                }
    
                MyEnum::Var2(v) => {
                    state.write_u8(2); // use any distinct number as an enum variant identifier
                    v.hash(state); // hash the actual value
                }
    
                MyEnum::Var3(v, _) => {
                    state.write_u8(3);
                    v.hash(state);
                }
            }
        }
    }
    
    impl PartialEq for MyEnum {
        fn eq(&self, other: &Self) -> bool {
            match (self, other) {
                (MyEnum::Var1, MyEnum::Var1) => true,
                (MyEnum::Var2(x), MyEnum::Var2(y)) => x == y,
                (MyEnum::Var3(x, _), MyEnum::Var3(y, _)) => x == y,
                _ => false,
            }
        }
    }
    
    impl Eq for MyEnum {}
    
    

    【讨论】:

      【解决方案2】:

      derivative crate 旨在为标准特征提供自动实现,并支持常见的自定义。有了这个板条箱,你就可以得到你想要的东西,看起来像这样:

      use std::collections::HashSet;
      use derivative::Derivative; // 2.2.0
      
      #[derive(Debug, Eq, Derivative)]
      #[derivative(PartialEq, Hash)]
      enum MyEnum {
          Var1,
          Var2(u32),
          Var3(
              u32, 
              #[derivative(PartialEq="ignore")]
              #[derivative(Hash="ignore")]
              u32
          ),
      }
      
      fn main() {
          let mut set = HashSet::new();
          set.insert(MyEnum::Var1);
          set.insert(MyEnum::Var1);
          set.insert(MyEnum::Var2(1));
          set.insert(MyEnum::Var2(2));
          set.insert(MyEnum::Var3(1, 1));
          set.insert(MyEnum::Var3(1, 1));
          set.insert(MyEnum::Var3(2, 1));
          set.insert(MyEnum::Var3(2, 2));
          
          println!("set = {:?}", set);
      }
      
      set = {Var1, Var3(1, 1), Var3(2, 1), Var2(2), Var2(1)}
      

      playground 上查看。

      【讨论】:

        猜你喜欢
        • 2018-03-31
        • 1970-01-01
        • 2012-12-13
        • 1970-01-01
        • 1970-01-01
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多