How best to capture an enum value, but with a guard to restrict the discriminant value?

I'd like to match for a subset of discriminants in an enum, but also capture the original enum variant (by which I mean discriminant and value together - not sure if I have the terms right). I can't quite figure out how to do both, the reduced example below describes the closest I can get, but in this case clippy doesn't understand the matches! guard and still fails the code with:

error[E0004]: non-exhaustive patterns: `X::A(_)` and `X::C(_)` not covered

...this makes me think I must not be doing this idiomatically. Any suggestions on the right way to work through this pattern?

#[derive(Debug, PartialEq)]
enum X {
    A(i32),
    B(i32),
    C(i32),
}

fn only_add_a_or_c(x: X, v: &mut Vec<X>) {
    match x {
        x if matches!(x,X::A(_) | X::C(_)) => {
            v.push(x);
        },
        X::B(_) => (),
    }
}

fn main() {
    let mut v = Vec::new();

    only_add_a_or_c(X::A(10),&mut v);
    only_add_a_or_c(X::B(20),&mut v);
    only_add_a_or_c(X::C(30),&mut v);

    assert_eq!(v, vec![X::A(10), X::C(30)]);    
}

I understand in this reduced form there are all kinds of workarounds like matching toB(_) first and then matching to everything else, but those kinds of options don't apply to the real case this is reduced from.

You can bind the whole enum value and also match it with the @ syntax:

match x {
    x @ (X::A(_) | X::C(_)) => {
        v.push(x);
    },

Or since there is already an x as an input to the match (this might not be true in your real situation) you can just not bind anything in the pattern. (This might be considered more, or less, clear, depending on the situation and the reader.)

match x {
    X::A(_) | X::C(_) => {
        v.push(x);
    },
3 Likes

Perfect, thank you!