Default match bindings
Have you ever had a borrowed Option<T>
and tried to match on it? Youprobably wrote this:
let s: &Option<String> = &Some("hello".to_string());
match s {
Some(s) => println!("s is: {}", s),
_ => (),
};
In Rust 2015, this would fail to compile, and you would have to write thefollowing instead:
// Rust 2015
let s: &Option<String> = &Some("hello".to_string());
match s {
&Some(ref s) => println!("s is: {}", s),
_ => (),
};
Rust 2018, by contrast, will infer the &
s and ref
s, and your originalcode will Just Work.
This affects not just match
, but patterns everywhere, such as in let
statements, closure arguments, and for
loops.
More details
The mental model of patterns has shifted a bit with this change, to bring itinto line with other aspects of the language. For example, when writing afor
loop, you can iterate over borrowed contents of a collection byborrowing the collection itself:
let my_vec: Vec<i32> = vec![0, 1, 2];
for x in &my_vec { ... }
The idea is that an &T
can be understood as a borrowed view of T
, andso when you iterate, match, or otherwise destructure a &T
you get aborrowed view of its internals as well.
More formally, patterns have a "binding mode," which is either by value(x
), by reference (ref x
), or by mutable reference (ref mut x
). In Rust2015, match
always started in by-value mode, and required you to explicitlywrite ref
or ref mut
in patterns to switch to a borrowing mode. In Rust2018, the type of the value being matched informs the binding mode, so thatif you match against an &Option<String>
with a Some
variant, you are putinto ref
mode automatically, giving you a borrowed view of the internaldata. Similarly, &mut Option<String>
would give you a ref mut
view.