Confusion about `match` and `patterns`

fn main() {
    let x = 'x';
    let c = 'c';
    
    match c {
        x => println!("x: {} c: {}", x, c),
    }
    
    println!("x: {}", x)
    
}

In my opinion, the value of expression c in match is 'c'. And it doesn't match the value of x, then it would not perform the println!.
Surprisingly! The code can run!
So the value of 'x' should be 'c'. How can this happen???

the result:
x: c c: c
x: x

1 Like

You are actually not comparing c to x, but capturing c in a new x. It's as if you would write let x = c;, but with the difference that it will check if the value of c matches the pattern in question.

If this was an actual comparison, you would get an error where the compiler complains about a non-exhaustive match, since it wouldn't cover all the possible scenarios.

I hope this makes sense.

Your words are very helpful.
the process is : let x = c, then use the value of x in the subsequent println!. am I right?
But I can't really get the meaning of

it will check if the value of c matches the pattern in question

what's the pattern in this situation, could you give me more details, i am a beginner.
many thanks!

It make sense to me but I think there should be a warning when you shadow an variable in a match pattern.
This can be very confusi

The pattern is, in this case, "any value x", or in Rust: x. It may be easier to demonstrate with a more complex value:

let maybe_c = Some('c');

match maybe_c {
    Some('a') => println!("found the 'a'"),
    Some(x) => println!("it was an '{}'", x),
    None => println!("found nothing"),
}

The patterns are whatever is on the left side of the => and a branch will be chosen depending on which one matches the actual value.

FYI, the left hand side of let x = c is also a pattern, but it must always match. This lets you destructure values:

struct Foo {
    a: u8,
    b: u8,
    c: u8,
}

//Create a value...
let foo = Foo {
    a: 1,
    b: 2,
    c: 3,
};

//...and take it apart
let Foo { a, b, c } = foo;

println!("a = {}, b = {}, c = {}", a, b, c);

In general you need to be careful about Rust shadowing things (in other cases Rust is too much scared of shadowing).

Shadowing is only a problem, in my experience, when using short/one letter names. Descriptive variable names lowers the risks immensely.

Edit: Actually, what I think helps is to think in a data-flow way, instead of just "let me put this somewhere". Names can be recycled if it's the same data, but not otherwise.

I got it.
the code is helpful, many thanks

This prints "it was an 'c'", but to me, it could have just as well printed "found the 'a'" because maybe_c is neither an x nor an 'a'. Why did the compiler prefer the Some(x) to the Some('a')? Neither has a variable binding such as "let x ...".

Simply because maybe_c is Some('c') and not Some('a').

It does, actually, but not as simple. It's similar to the post I did immediately after that one. This is the reasoning of the compiler:

  1. Does the value of maybe_c match a Some with an 'a'? No.
  2. Does the value of maybe_c match a Some with any value x? Yes, bind the value to x.

The binding is the same as if I could write let Some(x) = maybe_c, but that's impossible, since maybe_c can be None.

In the original code, "let x = 'x';", the x is not mutable. Yet the value of x is allowed to change to a c in the match statement. Can you speak to that? Later, with your "Some" statements, it looks like you are saying that any letter or group of letters can be substituted instead of x because Some(x) is defining the variable x. For example,
Some(abcde) => println!("it was an '{}'", abcde),
Going back to the original code with "x => println ...." being the only statement within the match clause, it looks like any valid variable name on the left of the => symbol will be taken as "I am a new variable, or I am redefining the named variable, and I am a catchall that will match anything". Is this understanding correct?

You got it more or less correct. The original is confusing because the name x is reused in a new variable. The match statement defines a new x that shadows the old one within that particular arm. The old x is still there, but it's temporarily unaccessible.