1

I'm trying to compare a variable of enum type stored in a vector of structs, with a variable of the same type passed into my function as a parameter. So, both enums are stored in variables. However, I'm getting unexpected results. I'm using the matches!() macro for comparison. Can anyone explain this behaviour?

enum Foo {
    A,
    B,
}


fn main() {
    let a = Foo::A;
    if matches!(a, Foo::A) { println!("expected") }
    if matches!(a, Foo::B) { println!("not expected 1") }
    if matches!(Foo::B, a) { println!("not expected 2") }

    let b =  Foo::B;
    if matches!(a, b) { println!("not expected 3") }
}

Output:

expected
not expected 2
not expected 3
E_net4
  • 27,810
  • 13
  • 101
  • 139
eldorz
  • 145
  • 1
  • 8

2 Answers2

5

The matches! macro is not symmetrical: the first operand is the expression to be tested and the second operand is a pattern to try matching the first operand against. If this order is not followed, the results will likely be unexpected.

While the first two matches! are well formed and do what you expect, the third and fourth one are most likely not what you want.

matches!(Foo::B, a) // test `Foo::B` against pattern `a`
matches!(a, b) // test `a` against pattern `b`

The expression to be tested is the literal value Foo::B, and the pattern is a new identifier, a in the third example and b in the fourth one. Since the pattern is only an identifier, it will match any expression. It does not relate at all to the variables a and b declared beforehand. The code below would still compile even when b did not exist.

let a = Foo::A;
matches!(a, b);

Note also that these if statements would print warnings, because a new variable was created from the pattern, but was not used.

warning: unused variable: `a`
 --> src/main.rs:7:25
  |
7 |     if matches!(Foo::B, a) { println!("not expected 2") }
  |                         ^ help: if this is intentional, prefix it with an underscore: `_a`
  |
  = note: `#[warn(unused_variables)]` on by default

See also:

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • Thank you! The links you provided should answer my follow-up question, which is how does one then compare enums held in variables in a match statement. I think the (n if n == a) pattern should work. – eldorz Oct 28 '21 at 23:44
0

Thank you to E_net4 the curator for the helpful answer. I can now get the behaviour I expect using the following:

#[derive(PartialEq)]
enum Foo {
    A,
    B,
}


fn main() {
    let a = Foo::A;
    let b = Foo::B;

    let s = match a {
        n if n == b => "a matches b",
        n if n == a => "a matches a",
        _ => "no match"
    };
    println!("{}", s);
}

With output:

a matches a
eldorz
  • 145
  • 1
  • 8
  • Note that there isn't much benefit from using a `match` expression in this example. The same can be achieved with a sequence of if-else expressions (e.g. `let s = if a == b { "a matches b" } else if a == a { "a matches a" } else { "no match" };`). `if` guards in match braces are useful when combined with more complex patterns. – E_net4 Oct 29 '21 at 14:45