8

In the below program I am trying to understand the intricacies of the match statements when mut, & do come in. Beyond that there is no other scope in terms of any functionality.

To figure out the type of the variable, I used the variable to call a function checkout(n:i32). The compiler now will complain that checkout expects i32, but whereas the program is passing some other type. I am a newbie and this was the one way i thought we could figure out types of a variable which we don't explicitly mention.

fn checkout (k5:i32) {}

fn main() {
    let k5 = "learning rust".to_string();
    let k1 = Some(k5);
    match &mut k1 {
        Some(mut n) => {
            checkout(n);
            println!("the idea is {} {} ", n, n);
        }
        None => {}
    }
}

If I compile the program, the following error message is got

error[E0308]: mismatched types
 --> suds12.rs:9:35
  |
9 |           Some(mut n)=> {checkout(n);println!("the idea is {} {} ",n,n);},
  |                                   ^ expected `i32`, found struct `String`

The above clearly demonstrates that when mut n is present within Some, then the type of n is found to be that of String.

However if the program changes from Some (mut n) to Some(n), then the type of n is said to be &mut string. This can be again seeing by compiling the program with Some(n) and the error message is given below

error[E0308]: mismatched types
 --> suds12.rs:9:31
  |
9 |           Some(n)=> {checkout(n);println!("the idea is {} {} ",n,n);},
  |                               ^ expected `i32`, found `&mut String`

Here we can easily see that n is of type &mut String.

Why this is so?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Sudarsan
  • 303
  • 2
  • 5
  • 1
    *"If i comple the above program"* - the program [does not compile](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea81c0898039acf8192d6d8d586a7620), `k1` should be marked `mut` and I'm guessing the other error is the source of this question? I'm asking because you did not mention any error. – kmdreko Jan 01 '22 at 09:50
  • Can you show how exactly you obtain the types? – mkrieger1 Jan 01 '22 at 09:53
  • @kmdreko: I have edited the program to mention how i figured out the type along with th compilation errors. – Sudarsan Jan 01 '22 at 10:17
  • Are you asking what the `mut` keyword does in a match statement? As far as I know it just derefs a mutable reference for exhaustiveness and convenience, no? – MeetTitan Jan 02 '22 at 02:45

1 Answers1

5

The confusion why Some(n) binds n to &mut String but Some(mut n) binds n to String is due to how match ergonomics were implemented.

Prior to this, match arms for references had to be much more explicit:

let mut k = Some("string".to_string());
match &mut k {
    &mut Some(ref mut x) => {},
    &mut None => {},
};

compile with version 1.25.0 or prior to test out the behavior

Lets focus on all the potential bindings for x:

  • x: would bind to the value directly, yielding a String
  • ref x: would bind to the value by reference, yielding a &String
  • ref mut x: would bind to the value by mutable reference, yielding a &mut String
  • mut x: is the same as x, it would bind to the value directly, yielding a String, with the only difference that n can be mutated

This understandably was tedious to write so RFC 2005: Match Ergonomics was designed to make the typical cases simpler to use. To accomplish that, it introduced binding modes where if the value was being pattern-matched on was a immutable or mutable reference, it would automatically infer bindings to x to be ref x or ref mut x respectively. So the above code could now be written as:

let mut k = Some("string".to_string());
match &mut k {
    Some(x) => {},
    None => {},
};

Much nicer.

However, the original syntax still exists and is usable even with the new binding modes in effect. For example, you can still use ref in a binding on a value to only bind to its reference. Or if you're already matching on a mutable reference you can use just ref to override that the binding to be immutable instead of mutable. Using any of ref/ref mut/mut would override the binding mode.

Do you see where this is going? Using Some(x) will use the ref mut binding mode since it is being matched on a &mut Option<_> and therefore bind x to &mut String. But using Some(mut x) the binding mode is overridden and will therefore bind x to String directly, with x being mutable.

See also:

kmdreko
  • 42,554
  • 6
  • 57
  • 106