Matching References
When the scrutinee of a match, if let, or let expression is a reference (&T or &mut T), Rust's match ergonomics (default binding modes) automatically propagate the reference down to pattern bindings so you can write clean constructor patterns (Some(val)) without manually writing & and ref at every nesting level.
How Match Ergonomics Works
When matching a reference (&T or &mut T) against a non-reference constructor pattern (such as Some(x) or Point { x, y }):
- The compiler automatically dereferences the scrutinee to test the constructor.
- The default binding mode shifts from by-value (
move) to by-reference (reffor&T, orref mutfor&mut T). - As a result,
xis bound as&Inner(or&mut Inner) rather than movingInnerout of the reference!
| Scrutinee Type | Pattern Written | Resulting Type of val |
|---|---|---|
opt: Option<String> (by value) |
Some(val) |
String (moved by value) |
opt: &Option<String> (shared ref) |
Some(val) |
&String (borrowed immutably via match ergonomics) |
opt: &mut Option<String> (mutable ref) |
Some(val) |
&mut String (borrowed mutably via match ergonomics) |
opt: &Option<i32> (i32: Copy) |
Some(&val) |
i32 (&val explicitly matches the reference and copies i32) |
fn main() {
let mut name: Option<String> = Some(String::from("Ada"));
// 1. Matching on `&name` binds `s` as `&String` (no move occurs):
if let Some(s) = &name {
println!("Read via &name: {s}");
}
// 2. Matching on `&mut name` binds `s` as `&mut String`:
if let Some(s) = &mut name {
s.push_str(" Lovelace");
}
println!("After mutation: {name:?}");
}
Read via &name: Ada
After mutation: Some("Ada Lovelace")
Stripping & in Patterns on Copy Types (&x)
When iterating over or matching a reference to a Copy type (such as opt: &Option<i32> or slice.iter() yielding &i32), matching with &x in the pattern peels off the reference and binds x as a plain i32 value instead of &i32:
fn sum_even(numbers: &[i32]) -> i32 {
let mut total = 0;
// `numbers.iter()` yields `&i32`; pattern `&n` destructures the reference so `n: i32`
for &n in numbers {
if n % 2 == 0 {
total += n;
}
}
total
}
fn main() {
println!("Sum of evens: {}", sum_even(&[1, 2, 3, 4, 6]));
}
Sum of evens: 12
Explicit ref and ref mut Bindings When Matching by Value
When the scrutinee itself is owned by value (or when one field of a struct should be moved while another field is borrowed in the same pattern), use the ref or ref mut keyword on individual bindings:
struct Job {
id: u64,
payload: String,
}
fn main() {
let mut job = Job {
id: 7,
payload: String::from("initial"),
};
// Match `job` without `&`, copying `id` by value while borrowing `payload` mutably:
match job {
Job {
id,
ref mut payload,
} => {
payload.push_str(" [processed]");
println!("Job #{id}: {payload}");
}
}
// `job` is still intact because `payload` was borrowed with `ref mut`, not moved!
println!("Final payload: {}", job.payload);
}
Job #7: initial [processed]
Final payload: initial [processed]
Rust 2024 Edition Match Ergonomics Refinements
In the Rust 2024 edition, match ergonomics enforces cleaner separation between explicit & / &mut reference patterns and implicit reference binding modes:
- Once a pattern shifts into an implicit reference binding mode (
reforref mut), writing&xinside that subpattern to peel a reference and simultaneously bind by reference is disallowed; instead, writexdirectly (or match on the explicit reference outer structure). muton a binding inside an implicit reference pattern no longer resets the binding mode back to by-value—it simply makes the local variable bindingmut.