Rust Refutable Patterns
A pattern is refutable when it can fail to match a value. A pattern is irrefutable when it matches every value.
A pattern that can never fail
A plain name matches every value you give it. A pattern that cannot fail is called irrefutable:
let x = 5;
let (a, b) = (1, 2);A pattern that can fail
Some(name) fits only when there is a value inside. For None it fails — a pattern that can fail is called refutable. match can use it, because every case has an arm:
let pet: Option<&str> = Some("Rex");
match pet {
Some(name) => println!("pet: {name}"),
None => println!("no pet"),
}Plain let refuses a refutable pattern
A plain let has no plan for the failing case, so the compiler stops you:
let Some(name) = pet;
// error E0005: refutable pattern in local bindingGive the failure a path
if let, while let and let ... else accept refutable patterns, because handling the failure is their job:
let pet: Option<&str> = None;
if let Some(name) = pet {
println!("pet: {name}");
} else {
println!("no pet");
}Example
fn main() {
let setting: Option<i32> = Some(7);
let Some(value) = setting else {
println!("no setting");
return;
};
println!("setting = {}", value);
let missing: Option<i32> = None;
if let Some(v) = missing {
println!("got {}", v);
} else {
println!("missing has no value");
}
}Key points
- let, for loops, and function parameters accept only irrefutable patterns.
- There is no sensible next step when the value does not fit.
- Match arms use refutable patterns.
- The last arm is the exception.
- It should catch everything left with an irrefutable one, like a name or _.
- Giving if let or let ... else a pattern that always matches earns a warning — the else side could never run
In this example, Some(value) is a refutable pattern: it fails for None. A plain let could not use it, so the code uses let ... else instead. setting is Some(7), so value becomes 7, the else block never runs, and setting = 7 prints. Below, if let tries the same kind of pattern on missing, which is None. The pattern fails, so the else block runs and prints missing has no value. Both constructs work because each one gives the failure somewhere to go.
Note
These names rarely drive a choice while writing code.
The names matter when an error names one: E0005 almost always means "this place needs a pattern that cannot fail".
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why does let Some(x) = some_option; fail to compile?