Patterns
A pattern names a shape. Place it where Rust accepts it. Know whether that place can refuse a value.
<!-- hal:authoritative:yaml -->
A pattern names a shape. Place it where Rust accepts it. Know whether that place can refuse a value.
§I - Frame
Duha session 22. Topics #22, TRPL Chapter 19: where patterns are valid, what makes a pattern refutable or irrefutable, and the syntax kit the book teaches. Session 21 taught OOP and Box<dyn Trait>. This row leaves polymorphism and opens pattern matching as control flow and binding.
A pattern matches the structure of types. Components include literals, destructured arrays / enums / structs / tuples, variables, wildcards, and placeholders. Examples: x, (a, 3), Some(Color::Red). Done-criteria: place a pattern in match / if let / while let / let and say refutable vs irrefutable.
Three moves land by the end:
- The Place Map: patterns live in
matcharms,let,if let,while let(plusforand function parameters). - The Refutability Gate: irrefutable always matches; refutable can fail; the construct picks which kind it accepts.
- The Syntax Kit: literals, names,
|,..=, destructuring,_/.., match guards, and@bindings.
Done-criteria: Can place a pattern in match / if let / while let / let and say refutable vs irrefutable.
§II - The Place Map
match is the formal home: keyword, a value, arms of PATTERN => EXPRESSION. Arms must be exhaustive. A catch-all name or _ covers what remains:
match x {
None => None,
Some(i) => Some(i + 1),
}
let has always been a pattern: let PATTERN = EXPRESSION;. Listing 19-1 destructures a tuple in one binding:
fn main() {
let (x, y, z) = (1, 2, 3);
}
Wrong arity fails at compile time (Listing 19-2). Fix with _ or .., or shrink the pattern.
if let (and else if let) is the short form when you care about one case. Listing 19-3 mixes if let, else if, and else if let across unrelated conditions. Unlike match, the compiler does not check exhaustiveness.
while let loops while the pattern matches. Listing 19-4 drains a channel with while let Ok(value) = rx.recv(). When the sender disconnects, recv returns Err and the loop stops.
for takes a pattern after the keyword (for (index, value) in v.iter().enumerate()). Function parameters are patterns too. The Done cell names the four seats above; for and parameters are siblings in the same chapter.
§III - The Refutability Gate
Irrefutable patterns match any value of the expected type. x in let x = 5; cannot fail. Refutable patterns can fail. Some(x) in if let Some(x) = a_value fails when the value is None.
Function parameters, plain let, and for accept only irrefutable patterns. if let, while let, and let ... else accept refutable ones (and warn on pointless irrefutable patterns, because the failure branch would be dead).
Listing 19-8 tries let Some(x) = some_option_value; and fails with E0005. Listing 19-9 fixes it with let ... else:
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value else {
return;
};
println!("{x}");
}
Match arms want refutable patterns except for the final irrefutable catch-all that seals exhaustiveness.
Thus the Refutability Gate: name the kind, then pick the construct that accepts it.
§IV - The Syntax Kit
Literals. Match a concrete value: 1 =>, _ =>.
Named variables. A name binds and, inside match / if let / while let, can shadow outer names (Listing 19-11).
Multiple patterns. | is or: 1 | 2 =>.
Ranges. Inclusive ..= for numeric or char values: 1..=5, 'a'..='j'.
Destructuring. Structs: let Point { x, y } = p; or mix literals and bindings (Point { x, y: 0 }, Listing 19-14). Enums follow the variant shape: unit, struct-like, or tuple-like (Listing 19-15).
Ignoring. _ matches without binding. Nested _ ignores a field. .. ignores the rest of a tuple, struct, or slice.
Match guards. Extra if after the pattern; the arm runs only when pattern and guard both hold:
fn main() {
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("{x} even"),
Some(x) => println!("{x} odd"),
None => (),
}
}
**@ bindings.** Test and bind in one pattern. Listing 19-29: Message::Hello { id: id @ 3..=7 } captures id only inside that range.
enum Message {
Hello { id: i32 },
}
fn main() {
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello { id: id @ 3..=7 } => {
println!("Found an id in range: {id}");
}
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range");
}
Message::Hello { id } => println!("Found some other id: {id}"),
}
}
§V - Proof and close
- Write one pattern each in
match,if let,while let, andlet. - Point at
Some(x)vs barexand say which is refutable. - Fix a refutable
letwithlet ... else(or switch toif let). - Use one destructuring pattern, one
_or.., one match guard, and one@binding.
Done-criteria: Can place a pattern in match / if let / while let / let and say refutable vs irrefutable.
Next: Unsafe and macros (Topics #23, TRPL Ch.20). Keep the Place Map; #23 opens unsafe plus macro_rules!.