如何在没有模式匹配的情况下比较枚举

Chr*_*oph 30 enums pattern-matching rust

我想申请filter一个迭代器,我想出了这个并且它可以工作,但它超级详细:

.filter(|ref my_struct| match my_struct.my_enum { Unknown => false, _ => true })
Run Code Online (Sandbox Code Playgroud)

我宁愿写这样的东西:

.filter(|ref my_struct| my_struct.my_enum != Unknown)
Run Code Online (Sandbox Code Playgroud)

这给了我一个编译错误

binary operation `!=` cannot be applied to type `MyEnum`
Run Code Online (Sandbox Code Playgroud)

有冗长模式匹配的替代方案吗?我寻找一个宏但找不到合适的宏.

Vla*_*eev 47

首先,您可以使用PartialEq特征,例如#[derive]:

#[derive(PartialEq)]
enum MyEnum { ... }
Run Code Online (Sandbox Code Playgroud)

然后你的"理想"变体将按原样运行.但是,这需要MyEnum实现内容PartialEq,这并不总是可行/想要的.

其次,你可以自己实现一个宏,就像这样(尽管这个宏不支持所有类型的模式,例如替代方案):

macro_rules! matches(
    ($e:expr, $p:pat) => (
        match $e {
            $p => true,
            _ => false
        }
    )
)
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它:

.filter(|ref my_struct| !matches!(my_struct.my_enum, Unknown))
Run Code Online (Sandbox Code Playgroud)

一个RFC将这样的宏添加到标准库中,但它仍在讨论中.

  • 为什么 Rust 必须与许多其他语言有很大不同。不可能是一个简单的 a == SomeEnum.Value nooooooo,必须有十亿个其他不同的宏和内部功能只是为了比较一个值。#抱歉咆哮 (14认同)
  • @Raid 在 Rust 中,枚举内部可能有嵌套值,它们不一定实现“PartialEq”。因此,对于 _任意_ 枚举,编译器无法自动派生 `PartialEq` (因此支持 `==` 运算符)。对于没有嵌套值的简单枚举,理论上来说,这是可以完成的,但是当您发展枚举并创建复杂的枚举变体时,您会得到令人惊讶的行为,然后突然所有相等比较都停止工作。Rust 遵循减少意外/增加明确性的原则,并要求您显式地编写“PartialEq”。 (14认同)
  • 更新:`matches!` 已添加到 1.42.0 中的 std 中。 (10认同)
  • 此外,这里没有**内部功能 - `#[derive(PartialEq)]` 和 `matches!` 都是稳定的公共 API,它们是标准库的一部分,并且有很好的文档记录。 (3认同)
  • @Raid 枚举可以携带值这一事实是 Rust 最令人惊奇的事情之一。它在很多情况下都很有用,例如 [`serde_json::Value`](https://docs.rs/serde_json/latest/serde_json/value/enum.Value.html)。因此,在我看来,默认情况下失去可比性是一个很小的代价,特别是如果你无论如何都可以使用“#[derive(PartialEq)]”来实现它。另外,你的咆哮读起来有点像“哦不,为什么 Rust 这种试图解决许多问题的语言与那些存在所有问题的语言不同?” (2认同)
  • @Rust 听起来像是一个新的问题。我们不应该讨论题外话。 (2认同)

Mih*_*x64 9

您可以使用if let Some(x) = option { then }具有相同if let构造但不解构的习语:

if let Unknown = my_struct.my_enum { false } else { true }
Run Code Online (Sandbox Code Playgroud)

  • 我很惊讶你不能仅仅 if my_struct.my_enum == enum_value (2认同)
  • @stu原因是枚举可以携带嵌套值,因此默认情况下它们不能与“==”进行比较,因为它们的嵌套值可能不会实现“Eq”。 (2认同)

She*_*ter 7

我会使用模式匹配,但我会将它移动到枚举上的一个方法,以便过滤器闭包更整洁:

#[derive(Debug)]
enum Thing {
    One(i32),
    Two(String),
    Unknown,
}

impl Thing {
    fn is_unknown(&self) -> bool {
        match *self {
            Thing::Unknown => true,
            _ => false,
        }
    }
}

fn main() {
    let things = vec![Thing::One(42), Thing::Two("hello".into()), Thing::Unknown];
    for t in things.iter().filter(|s| !s.is_unknown()) {
        println!("{:?}", t);
    }
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:


Kep*_*ian 5

另一种选择是使用std::discriminant,如果您不关心匹配enum数据,这会很有用。看操场

use std::mem;

#[derive(Debug, PartialEq, Eq)]
enum Thing {
    One(i32),
    Two(String),
    Unknown,
}

impl Thing {
    fn is_unknown(&self) -> bool {
        mem::discriminant(self) == mem::discriminant(&Thing::Unknown)
    }
    
    fn is_one(&self) -> bool {
        mem::discriminant(self) == mem::discriminant(&Thing::One(i32::default()))
    }
}

fn main() {
    let things = [Thing::One(42), Thing::Two("hello".to_owned()), Thing::Unknown];
    
    let known_things = things.iter().filter(|&s| !s.is_unknown());
    assert!(known_things.eq([Thing::One(42), Thing::Two("hello".to_owned())].iter()));
    
    let ones = things.iter().filter(|&s| s.is_one());
    assert!(ones.eq([Thing::One(42)].iter()));
}
Run Code Online (Sandbox Code Playgroud)