Rust RFC 2005 "Match Ergonomics" was just accepted. However there was a copy-paste error in the summary that led to some confusion about what it was actually doing. Here's a quick run down of the details that matter to users:
TL;DR version:
Before this change, matching on references works like this:
let by_ref: &MyEnum;
let by_mut: &mut MyEnum;
match by_ref { A( x) => // Error: mismatched types
match *by_ref { A(ref x) => // by reference
match by_ref { &A(ref x) => // by reference
match by_mut { A( x) => // Error: mismatched types
match *by_mut { A(ref mut x) => // by mutable reference
match by_mut { &mut A(ref mut x) => // by mutable reference
After this change, everything remains the same except these two now Just Work:
match by_ref { A(x) => // by reference
match by_mut { A(x) => // by mutable reference
Long version:
Before this change, there are many ways to precisely match a variable, and bind its contents. For instance, with these variables:
let mut by_val: MyEnum;
let by_ref: &MyEnum;
let by_mut: &mut MyEnum;
You get the following behaviours:
match by_val { A( x) => // by value (move)
match by_val { A(ref x) => // by reference
match by_val { A(ref mut x) => // by mutable reference
match by_ref { A( x) => // Error: mismatched types
match *by_ref { A( x) => // Error: cannot move out of borrow -- unless Copy, then by value (copy)
match *by_ref { A(ref x) => // by reference
match by_ref { &A( x) => // Error: cannot move out of borrow -- unless Copy, then by value (copy)
match by_ref { &A(ref x) => // by reference
match by_mut { A( x) => // Error: mismatched types
match *by_mut { A( x) => // Error: cannot move out of borrow -- unless Copy, then by value (copy)
match *by_mut { A(ref x) => // by reference
match *by_mut { A(ref mut x) => // by mutable reference
match by_mut { &mut A( x) => // Error: cannot move out of borrow -- unless Copy, then by value (copy)
match by_mut { &mut A(ref x) => // by reference
match by_mut { &mut A(ref mut x) => // by mutable reference
After this change all cases remain the same, except these two cases now compile:
match by_ref { A(x) => // by reference
match by_mut { A(x) => // by mutable reference
FAQ:
It's the same as if you used ref
or ref mut
-- &T
or &mut T
respectively.
No. Only the pattern and the input type will be considered when deciding the type of x
.
Potentially, but unlikely. There are two hypothetical scenarios where this will cause problems:
- when you think you're using this new sugar, but aren't
- when you think you aren't using this sugar, but are
Most cases will lead to some kind of error:
match by_val { A(x) => { x = y; } // error: x must be mutable,
// *or* later on -- error: by_val moved into x
match by_mut { A(x) => { x = y; } // error: expected &mut T, found T
The most plausible error would probably be:
// Thought we were making a copy and mutating that
match by_mut { A(x) => { x.y = z } // OK!
Note however that if the user remembered they should have to make x mut, they'll get a warning:
// Thought we were making a copy and mutating that
match by_mut { A(mut x) => { x.y = z } // warning: x is mutable but isn't mutated
And note that this typo is already possible:
// Oops forgot ref
match *by_mut { A(mut x) => { x.y = z; } // If T is Copy, OK -- writes to the temporary
So all in all, a careless developer can definitely shoot themselves in the foot, especially if they blindly add/remove refs/muts to get their code to work... but that was already true of patterns.