Understanding Pattern Refutability in Rust
Patterns in Rust are categorized into two types: refutable and irrefutable. An irrefutable pattern matches every conceivable value supplied to it. For instance, in the statement let x = 5;, the identifier x serves as an irrefutable pattern since it can accommodate any value without failing.
Conversely, a refutable pattern may fail to match certain values. Consider the pattern Some(x) within an if let Some(x) = a_value expression. If a_value holds None instead of Some, the pattern fails to match.
Only irrefutable patterns are permitted in function parameters, let statements, and for loops becuase these construcst expect guaranteed matches for normal program execution. In contrast, if let and while let expressions require refutable patterns by design, as they inherently handle potential matching failures through conditional branching.
In practice, developers rarely need to consider refutability explicitly. However, understanding this concept becomes essential when encountering related compiler diagnostics. Depending on intended behavior, resolving such issues involves either altering the pattern or restructuring the surrounding code.
Consider attempting to use a refutable pattern like Some(x) in a let statement:
let Some(x) = some_option_value;
This fails compilation because let demands an irrefutable pattern. Should some_option_value contain None, the Some(x) pattern would not match. The Rust compiler reports this error accordingly:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
To resolve using a refutable pattern where an irrefutable one is expected, replace let with if let. This allows skipping execution of the block when matching fails:
# let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
println!("{}", x);
}
Alternatively, applying an irrefutable pattern such as x in a if let context triggers a warning:
if let x = 5 {
println!("{}", x);
};
The compiler warns about misuse since if let should evaluate potentially fallible conditions:
warning: irrefutable if-let pattern
--> <anon>:2:5
|
2 | / if let x = 5 {
3 | | println!("{}", x);
4 | | };
| |___^
|
= note: #[warn(irrefutable_let_patterns)] on by default
In match expressions, all arms except the final one must specify refutable patterns. The last arm typically uses an irrefutable catch-all pattern to ensure completeness. While Rust permits single-arm matches with irrefutable patterns, doing so offers no advantage over simpler alternatives like direct assignment via let.