Enums and Pattern Matching
Refutable vs. Irrefutable Patterns
Every pattern in Rust is classified as either irrefutable (guaranteed at compile time to match every possible value of its input type) or refutable (capable of failing to match for some possible runtime values).
Where Each Pattern Kind Is Accepted
| Language Construct | Accepts Irrefutable Patterns? | Accepts Refutable Patterns? | Reason |
|---|---|---|---|
let pat = expr; |
Yes | No (E0005) |
Has no fallback branch if the pattern fails |
fn f(pat: Type) / |pat| |
Yes | No (E0005) |
Function call has no fallback branch if argument fails to match |
for pat in iter |
Yes | No (E0005) |
Loop runs for every yielded item |
let pat = expr else { ... }; |
Warns (irrefutable_let_patterns) |
Yes | else block handles mismatch |
if let pat = expr |
Warns (irrefutable_let_patterns) |
Yes | Condition is meant to branch on potential mismatch |
while let pat = expr |
Warns (irrefutable_let_patterns) |
Yes | Loop terminates when pattern stops matching |
match expr { pat => ... } |
Yes (as the final catch-all arm) | Yes (as individual arms) | Arms test refutable cases until all cases are covered |
Examples of Irrefutable vs. Refutable Patterns
| Pattern | Scrutinee Type | Classification | Why |
|---|---|---|---|
x, _, (a, b) |
(i32, i32) |
Irrefutable | Every (i32, i32) is a 2-tuple |
Point { x, y } |
struct Point { x: i32, y: i32 } |
Irrefutable | A struct has only one shape |
[a, b, c] |
[i32; 3] (fixed array) |
Irrefutable | Array length 3 is guaranteed at compile time |
[a, b, c] |
&[i32] (slice) |
Refutable | Slice length at runtime might not be 3 |
Some(x) |
Option<i32> |
Refutable | Value might be None at runtime |
Ok(v) |
Result<T, !> (or uninhabited Err) |
Irrefutable | Because ! has zero values, Err can never exist |
Error: Using a Refutable Pattern in let (E0005)
Attempting to unpack Some(x) in a plain let statement fails because let has no way to handle None:
// ❌ does not compile
fn main() {
let maybe_port: Option<u16> = Some(8080);
let Some(port) = maybe_port;
println!("{port}");
}
error[E0005]: refutable pattern in local binding
--> src/main.rs:3:9
|
3 | let Some(port) = maybe_port;
| ^^^^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
help: you might want to use `let else` to handle the variant that isn't matched
|
3 | let Some(port) = maybe_port else { todo!() };
| ++++++++++++++++
Fix: Use if let or let-else for Refutable Patterns
fn main() {
let maybe_port: Option<u16> = Some(8080);
// Fix 1: `let-else` when you want `port` bound in the outer scope:
let Some(port) = maybe_port else {
return;
};
println!("Listening on port {port}");
// Fix 2: Irrefutable destructuring of a single-variant newtype or tuple in `let`:
struct Meters(f64);
let Meters(distance) = Meters(42.5);
println!("Distance: {distance} m");
}
Listening on port 8080
Distance: 42.5 m