Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Match shadowing example in the Patterns section of the Rust book is very perplexing

Tags:

rust

In learning Rust, I encountered the following in the official Rust book:

There’s one pitfall with patterns: like anything that introduces a new binding, they introduce shadowing. For example:

let x = 'x';
let c = 'c';

match c {
    x => println!("x: {} c: {}", x, c),
}

println!("x: {}", x)

This prints:

x: c c: c
x: x

In other words, x => matches the pattern and introduces a new binding named x that’s in scope for the match arm. Because we already have a binding named x, this new x shadows it.

I don't understand two things:

  1. Why does the match succeed?
    Shouldn't the differing value of c and x cause this to fail?
  2. How does the match arm x binding get set to 'c'?
    Is that somehow the return of the println! expression?
like image 973
Michael Plotke Avatar asked Feb 22 '16 20:02

Michael Plotke


Video Answer


2 Answers

There is a fundamental misconception of what match is about.

Pattern-matching is NOT about matching on values but about matching on patterns, as the name imply. For convenience and safety, it also allows binding names to the innards of the matched pattern:

match some_option {
    Some(x) => println!("Some({})", x),
    None    => println!("None"),
}

For convenience, match is extended to match the values when matching specifically against literals (integrals or booleans), which I think is at the root of your confusion.

Why? Because a match must be exhaustive!

match expressions are there so the compiler can guarantee that you handle all possibilities; checking that you handle all patterns is easy because they are under the compiler's control, checking that you handle all values is hard in the presence of custom equality operators.


When using just a name in the match clause, you create an irrefutable pattern: a pattern that cannot fail, ever. In this case, the entire value being matched is bound to this name.

You can exhibit this by adding a second match clause afterward, the compiler will warn that the latter binding is unreachable:

fn main() {
    let x = 42;
    match x {
        name => println!("{}", name),
        _    => println!("Other"),
    };
}

<anon>:6:5: 6:6 error: unreachable pattern [E0001]
<anon>:6         _    => println!("Other"),
                 ^

Combined with the shadowing rules, which specifically allow hiding a binding in a scope by reusing its name to bind another value, you get the example:

  • within the match arm, x is bound to the value of 'c'
  • after the arm, the only x in scope is the original one bound to the value 'x'
like image 134
Matthieu M. Avatar answered Oct 21 '22 20:10

Matthieu M.


Your two points are caused by the same root problem. Coincidentally, the reason that this section exists is to point out the problem you asking about! I'm afraid that I'm basically going to regurgitate what the book says, with different words.

Check out this sample:

match some_variable {
    a_name => {},
}

In this case, the match arm will always succeed. Regardless of the value in some_variable, it will always be bound to the name a_name inside that match arm. It's important to get this part first — the name of the variable that is bound has no relation to anything outside of the match.

Now we turn to your example:

match c {
    x => println!("x: {} c: {}", x, c),
}

The exact same logic applies. The match arm with always match, and regardless of the value of c, it will always be bound to the name x inside the arm.

The value of x from the outer scope ('x' in this case) has no bearing whatsoever in a pattern match.


If you wanted to use the value of x to control the pattern match, you can use a match guard:

match c {
    a if a == x => println!("yep"),
    _ => println!("nope"),
}

Note that in the match guard (if a == x), the variable bindings a and x go back to acting like normal variables that you can test.

like image 37
Shepmaster Avatar answered Oct 21 '22 18:10

Shepmaster