My misunderstanding of how match works

Hello! I'm a total Rust n00b and I'm reading the Rust Programming Language book and I'm up to the match operator. I don't understand why these two examples behave differently.

In this first example nothing matched is printed as I expected. :partying_face:

fn main() {
    let x = 5;
    match x {
        7 => println!("seven"),
        _ => println!("nothing matched!")
    }
}

But why is it that in this second example seven is printed? All I did was replace the value 7 with a variable containing the value 7 :thinking:

fn main() {
    let x = 5;
    let y = 7;
    match x {
        y => println!("seven"),
        _ => println!("nothing matched!")
    }
}

Here is what I think is happening. In the first example matching happens based on the value(s) of the arm(s) while in the second example matching happens based on type. Assuming what I think is happening is correct I don't understand why using a variable changes the evaluation from value to type.

Thank you for your consideration of my n00b question. :grin:

The thing on the left (7 in the first example, y in the second) is a pattern. 7 is a pattern that matches when the thing is 7; y is a pattern that always matches and binds the value to the name y.
If you wanted to match on values that are equal to a variable, you'd do

match x {
    x if x == y => println!("seven"),
    _ => println!("nothing matched!"),
}

Basically, the pattern describes the shape that it's matching on, and an identifier will match anything (this is useful because you can nest patterns, so (a, 5) is a pattern that matches a tuple where the second element is 5 and binds the first value to the name a.)

1 Like

The compiler emits a bunch of warning that should hopefully make things a bit clearer:

warning: unreachable pattern
 --> src/main.rs:6:9
  |
5 |         y => println!("seven"),
  |         - matches any value
6 |         _ => println!("nothing matched!")
  |         ^ unreachable pattern
  |
  = note: `#[warn(unreachable_patterns)]` on by default

warning: unused variable: `y`
 --> src/main.rs:3:9
  |
3 |     let y = 7;
  |         ^ help: if this is intentional, prefix it with an underscore: `_y`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `y`
 --> src/main.rs:5:9
  |
5 |         y => println!("seven"),
  |         ^ help: if this is intentional, prefix it with an underscore: `_y`

warning: 3 warnings emitted

(playground)

Both ys are unused, which means that the second y introduces a new variable that is unrelated (and shadows) the first variable. A better message may be along the lines of warning: pattern introduces a name that shadows a variable.

1 Like

Note also, that you can (sometimes) match against a constant value as you originally intended:

fn main() {
    const Y: usize = 10;
    
    match 10 {
        Y => println!("10"),
        _ => println!("Not 10")
    }
}

Prints "10".

This behaviour is controlled by StructuralEq and StructuralPartialEq, and hence something like this will not work (as a const match):

struct Foo(usize);

const BAR: Foo = Foo(10);

match Foo(10) {
    BAR => println!("10"),
    _ => println!("Not 10"),
}

Implementing StructuralEq and StructuralPartialEq through the #[derive]s works however.

1 Like

I didn't read the warnings because I'm executing this in a file with a bunch of toy functions from the book that aren't used so I get a bunch of warnings. :shushing_face:

What's interesting to me is that I woke up this morning with the sudden realization of what you've described (i.e., shadowing). So before I got on here I tweaked the code to test if y was being shadowed. This realization puts be back into a head space where I'm back to being unsure how match works in my example. But a lot of info from all you beautiful ppl have been provided so here is how I think it works now. Please confirm/deny.

In a match block the use of a variable will ALWAYS match and capture (or be bound to) the value being matched against? :thinking:

Yes, I believe that’s correct. One way to think about this is that patterns always behave the same way, and let x = something() is also a pattern-matching construct, so

match some_function() {
    Some(x) => { /* ... */ }
    None => { /* ... */ }
}

is roughly equivalent to

let tmp = some_function();
if let Some(x) = tmp { /* ... */ }
else if let None = tmp { /* ... */ }
else { unreachable!() }

Yes, unless some other pattern matches it first. For example:

match x {
    42 => println!("You guess the right answer!"),
    y => println!("You guessed {}.  Guess again.", y),
}

The patterns are checked in order from top to bottom. If x is 42, the first pattern will match, and the second one won't be checked. If x is any other value, then the first pattern will fail, and the second pattern will match (because y is a pattern that matches any value).

Patterns comprising a single identifier are an example of irrefutable patterns, which

always match the value they are being matched against

and make the following match arms unreachable, exactly as what you said :slight_smile:

Think of:

match x {
    42 => foo(),
    y => bar(),
}

as being the same as:

match x {
    42 => foo(),
    _ => bar(),
}

except the first one binds a new variable y that you can use in that arm only.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.