One of the most powerful features of Rust is pattern matching, implemented via the match statement. With the release of Rust 1.26, the term "match ergonomics" was introduced — a new logic for unpacking and automatic dereferencing of reference values within match expressions, aimed at reducing the number of explicit ref and * in patterns.
Before the advent of match ergonomics, when using enums that wrap a value in a reference (for example, Option<&T>), you had to explicitly indicate dereferencing in the pattern:
let opt: Option<&i32> = Some(&10); match opt { Some(&val) => { /* ... */ }, None => {}, }
This reduced readability and increased the likelihood of making mistakes with dereferencing levels in complex structures.
With the introduction of match ergonomics, Rust automatically dereferences references in patterns, allowing you to write simpler and more natural code:
let opt: Option<&i32> = Some(&10); match opt { Some(val) => println!("{}", val), None => (), }
The compiler "understands" that it needs to dereference &i32 to i32 for the convenience of the programmer. This feature significantly eases working with Option and Result types and their combinations with references.
Key Features:
* and ref in patternsWhat is the difference between using ref in a pattern and a reference to a value (for example, ref val vs &val)?
ref creates a reference to a value on the stack, whereas &val matches an already existing reference. This is important, for instance, if you need an additional reference for handling mutability.
Example:
let x = 5; match x { ref r => println!("ref: {}", r), // r: &i32 }
What happens if you use a mutable reference in pattern matching with Option<&T>?
Automatic dereferencing also works with mutable references. If you have Option<&mut T>, you can get the mutable variable directly through match.
Example:
let mut x = 5; let opt = Some(&mut x); match opt { Some(val) => *val += 1, None => {} }
Can match ergonomics lead to unexpected borrowings or ownership errors?
Yes, if you are not aware that a temporary borrowing is created inside the match which persists throughout the matching branch and can block other operations with that value.
* and ref, ignoring match ergonomics, which reduces the brevity and clarity of the codeA newcomer continues to use the old style Some(&val) or Some(ref val), getting errors or unpredictable behavior when updating Rust to a newer version.
Pros: Code works on older versions of Rust and is understandable to those who learned before match ergonomics.
Cons: Low expressiveness, risk of errors after compiler updates.
A programmer uses modern match ergonomics, keeping the code compact and effectively using automatic dereferencing.
Pros: Conciseness, easy to read, less risk of errors with reference levels.
Cons: This code will not work for older versions of Rust and may cause confusion for those who have little experience with modern idioms