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?
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 Stringref x: would bind to the value by reference, yielding a &Stringref mut x: would bind to the value by mutable reference, yielding a &mut Stringmut x: is the same as x, it would bind to the value directly, yielding a String, with the only difference that n can be mutatedThis 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:
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