如何为包含特殊情况的枚举实现 `Hash`?

7

I have this enum:

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

重要的是,我不想详尽地匹配每个枚举变量,因为我想保留所有其他枚举变量的默认行为。因此,据我所知,这些答案并不能满足我的需求。

我提出的解决方案如下:

#[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)
        }
    }
}

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

2个回答

9

Derivative 是一个用于提供标准 trait 的自动实现且支持通用定制的 crate。使用此 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 上查看。



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接