In Rust, patterns are used to destructure values, bind variables, and control program flow. The concept of 'refutability' describes whether a given pattern is guaranteed to match any possible value of its type or if it might fail to match certain values.
There are two types of patterns based on refutability:
1. Irrefutable Patterns (Guaranteed to Match):
An irrefutable pattern is one that cannot fail to match a value of the type it's being compared against. These patterns always succeed. They are typically used in contexts where a match is expected to always occur, such as `let` statements.
* Examples of Irrefutable Patterns:
* Variable bindings (e.g., `x`, `_` (wildcard))
* Structs or tuples where all fields are covered by irrefutable patterns (e.g., `Point { x, y }`, `(a, b)`)
* Slices that match the entire slice (e.g., `&[a, b, ..]`)
2. Refutable Patterns (Might Fail to Match):
A refutable pattern is one that can potentially fail to match a given value. These patterns are used when you only care about specific cases or variants of a type. They are commonly used in `if let` expressions and `match` arms, where handling of non-matching cases is explicitly allowed or required.
* Examples of Refutable Patterns:
* Literal values (e.g., `5`, `'A'`, `"hello"`)
* Specific enum variants (e.g., `Some(x)`, `Err(e)`)
* Structs with specific field values (e.g., `Point { x: 0, y }`)
* Range patterns (e.g., `1..=5`)
Contexts and Refutability Rules:
* `let PATTERN = EXPRESSION;`: This construct *requires* an irrefutable pattern. If you use a refutable pattern here, the compiler will raise an error because `let` expects to always successfully bind the variables. If a refutable pattern could fail, it would mean the variables might not be initialized, which Rust's `let` statement does not permit.
* `if let PATTERN = EXPRESSION { ... } else { ... }`: This construct *allows* refutable patterns. It's designed specifically to handle cases where a pattern might or might not match. If the pattern matches, the `if` block executes; otherwise, the `else` block (if present) executes, or nothing happens.
* `while let PATTERN = EXPRESSION { ... }`: Similar to `if let`, this loop continues to execute as long as the refutable pattern matches the expression.
* `match EXPRESSION { PATTERN => { ... }, ... }`: Each arm of a `match` statement uses a refutable pattern. The Rust compiler ensures that `match` statements are *exhaustive*, meaning all possible cases for the `EXPRESSION`'s type are covered by at least one pattern arm. If not all cases are explicitly covered, the compiler requires a wildcard pattern (`_`) as the last arm to catch any remaining values, effectively making the `match` statement exhaustive.
Example Code
use std::fmt::Debug;
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process_value<T: Debug>(val: T) {
println!("Processing: {:?}", val);
}
fn main() {
// --- Irrefutable Patterns ---
// 1. Variable binding: 'x' is irrefutable, matches any i32.
let x = 10;
println!("let x = {}; (irrefutable)", x);
// 2. Tuple destructuring: '(a, b)' is irrefutable for any 2-tuple.
let (a, b) = (1, 2);
println!("let (a, b) = ({}, {}); (irrefutable)", a, b);
// 3. Struct destructuring with all fields as variable bindings: 'p' is irrefutable for any Point.
struct Point { x: i32, y: i32 }
let p = Point { x: 5, y: 7 };
let Point { x: px, y: py } = p;
println!("let Point {{ x: px, y: py }} = {{ x:{}, y:{} }}; (irrefutable) -> px={}, py={}", p.x, p.y, px, py);
// --- Refutable Patterns ---
// Attempting to use a refutable pattern with `let` will result in a compile error.
// Uncomment the line below to see the error: "refutable pattern in local binding"
// let Some(value) = Some(10); // ERROR!
let some_num = Some(20);
let none_num: Option<i32> = None;
// 1. 'if let' for refutable patterns (enum variants)
if let Some(val) = some_num {
println!("if let Some({}) = some_num; (refutable) -> Matched Some", val);
} else {
println!("if let Some({}) = some_num; (refutable) -> Did not match Some", 0); // Placeholder for None case
}
if let Some(val) = none_num {
println!("if let Some({}) = none_num; (refutable) -> Matched Some", val);
} else {
println!("if let Some({}) = none_num; (refutable) -> Did not match Some", 0); // Placeholder for None case
}
// 2. 'match' statement for refutable patterns (enum variants, literals, ranges)
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => {
println!("Match: Quit message received.");
}
Message::Move { x, y } => {
println!("Match: Move to x:{}, y:{}.", x, y);
process_value(x); // Example usage of bound variables
}
Message::Write(text) => {
println!("Match: Writing text: {}.", text);
}
// Refutable pattern: specific field values & ranges combined
Message::ChangeColor(0..=255, 0, b) if b > 100 => {
println!("Match: Change color to R, G=0, B > 100.");
}
// Irrefutable pattern as a catch-all for remaining cases
_ => {
println!("Match: Some other message.");
}
}
let status_code = 200;
match status_code {
// Refutable pattern: literal
200 => println!("Match: OK"),
// Refutable pattern: range
400..=499 => println!("Match: Client error"),
// Refutable pattern: single wildcard (irrefutable for the rest)
_ => println!("Match: Unknown status"),
}
// 3. 'while let' for refutable patterns
let mut optional_value = Some(3);
let mut counter = 0;
while let Some(value) = optional_value {
println!("While let: Current value: {}", value);
counter += 1;
if counter == 2 {
optional_value = None; // Break the loop
}
}
println!("While let: Loop finished. Counter: {}", counter);
}








Refutability: Whether a Pattern Might Fail to Match