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 namedx
that’s in scope for the match arm. Because we already have a binding namedx
, this newx
shadows it.
I don't understand two things:
c
and x
cause this to fail?x
binding get set to 'c'
?println!
expression?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:
match
arm, x
is bound to the value of 'c'
x
in scope is the original one bound to the value 'x'
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With