88

I want to apply filter on an iterator and I came up with this one and it works, but it's super verbose:

.filter(|ref my_struct| match my_struct.my_enum { Unknown => false, _ => true })

I would rather write something like this:

.filter(|ref my_struct| my_struct.my_enum != Unknown)

This gives me a compile error

binary operation `!=` cannot be applied to type `MyEnum`

Is there an alternative to the verbose pattern matching? I looked for a macro but couldn't find a suitable one.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Christoph
  • 26,519
  • 28
  • 95
  • 133

4 Answers4

156

Use matches!, e.g.:

matches!(my_struct.my_enum, Unknown)

Alternatively, you can use PartialEq trait, for example, by #[derive]:

#[derive(PartialEq)]
enum MyEnum { ... }

Then your "ideal" variant will work as is. However, this requires that MyEnum's contents also implement PartialEq, which is not always possible/wanted.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 9
    Why must Rust be WILDLY different from many other languages. Couldn't have been an easy a == SomeEnum.Value noooooo, gotta have a billion other different macros and internal functionality just to compare a value. #SorryRanting – Raid Aug 23 '22 at 07:02
  • 13
    @Raid in Rust, enums may have nested values inside, which do not necessarily implement `PartialEq`. Therefore, for an _arbitrary_ enum, the compiler just can't derive `PartialEq` (and therefore support for the `==` operator) automatically. For simple enums without nested values, in theory, this could've been done, but then you will get surprising behavior when you evolve your enum and create a complex enum variant and then suddenly all equality comparisons stop working. Rust goes for the principle of less surprise/greater explicitness and requires you to write `PartialEq` explicitly. – Vladimir Matveev Sep 01 '22 at 00:26
  • 2
    Also, there is **no** internal functionality here - both `#[derive(PartialEq)]` and `matches!` are stable, public API, which are a part of the standard library and are very well documented. – Vladimir Matveev Sep 01 '22 at 00:27
  • 1
    Happen to know how to pass a Box to a parameter in a function expecting a Box, but also allowing me to store that reference of a Box in a different struct? I can't change an API, but also need access to that Box as well. While I have someone that knows Rust here.. lol :) (if you don't mind) – Raid Sep 02 '22 at 04:17
  • 2
    @Raid The fact that enums can carry values is one of the most amazing things about Rust. It's useful in sooo many situations, take for example [`serde_json::Value`](https://docs.rs/serde_json/latest/serde_json/value/enum.Value.html). So losing comparability by default is such a small price to pay, in my opinion, especially if you can achieve it anyway with `#[derive(PartialEq)]`. Also, your rant reads a little like "Oh no, why is Rust, a language who tries to fix many problems, different than the languages that have all the problems?" – Finomnis Sep 06 '22 at 17:22
  • 1
    @Finomnis Oh, no I get that. It's pretty useful now that I know what it all does. Just missing that normal compare for us noobs. Happen to know the question I asked Vlad? (The comment just before) about the Box ? :D – Raid Sep 06 '22 at 20:58
  • 2
    @Rust Sounds like a new SO question. We shouldn't discuss off-topic. – Finomnis Sep 07 '22 at 05:32
19

I'd use pattern matching, but I'd move it to a method on the enum so that the filter closure is tidier:

#[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 = [Thing::One(42), Thing::Two("hello".into()), Thing::Unknown];
    for t in things.iter().filter(|s| !s.is_unknown()) {
        println!("{:?}", t);
    }
}

You can combine this with the matches macro as well:

fn is_unknown(&self) -> bool {
    matches!(self, Thing::Unknown)
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Simple and elegant. It always seems counter-intuitive (for a Rust newbie like myself) that one can't just compare directly, but the "use pattern matching in a method" approach really goes a long way in a few short lines toward encouraging more testable, modular code.. – kevlarr Mar 23 '19 at 11:02
  • @Shepmaster lovely! I have just learnt about Enum's _impl_ and then, I see this! Good stuff! – Nirmalya Apr 03 '23 at 14:05
8

You can use if let Some(x) = option { then } idiom with the same if let construct but without destructuring:

if let Unknown = my_struct.my_enum { false } else { true }
Miha_x64
  • 5,973
  • 1
  • 41
  • 63
5

Another option would be to use std::discriminant, which is useful if you don't care about matching enum data. See the playground:

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()));
}
keplerian
  • 512
  • 1
  • 8
  • 19