Enums and Pattern Matching
Matching Multiple Patterns
The or-pattern operator (|) combines two or more patterns into a single pattern (PatternA | PatternB) that succeeds if the scrutinee matches any of the listed alternatives.
Top-Level and Nested Or-Patterns
In Rust, | can be used both at the top level of a match arm and nested inside struct, tuple, slice, reference, or enum variant patterns (Some(1 | 2 | 3)):
enum KeyEvent {
Char(char),
Special { code: u16, shift: bool },
}
fn handle_key(event: &KeyEvent) -> &'static str {
match event {
// Top-level `|` combining multiple patterns:
KeyEvent::Char('q' | 'Q') | KeyEvent::Special { code: 27, .. } => "Quit application",
// Nested `|` inside an enum variant payload:
KeyEvent::Char('w' | 'a' | 's' | 'd') => "Move player",
// Nested `|` with ranges:
KeyEvent::Char('0'..='9' | '+' | '-') => "Numeric input",
_ => "Unhandled key",
}
}
fn main() {
println!("'Q' -> {}", handle_key(&KeyEvent::Char('Q')));
println!("Esc -> {}", handle_key(&KeyEvent::Special { code: 27, shift: false }));
println!("'w' -> {}", handle_key(&KeyEvent::Char('w')));
println!("'7' -> {}", handle_key(&KeyEvent::Char('7')));
}
'Q' -> Quit application
Esc -> Quit application
'w' -> Move player
'7' -> Numeric input
Binding Variables Across | Alternatives
When an or-pattern P1 | P2 binds variables, the compiler enforces three strict consistency rules across every alternative separated by |:
- Same variable names: Every variable bound in
P1must also be bound inP2(E0408if missing). - Same types: Each bound variable must have the exact same type in every alternative (
E0308if mismatched). - Same binding mode: A variable cannot be bound by value in
P1and by reference inP2(E0409).
enum MetricEvent {
CpuAlert { host: String, percent: u8 },
MemAlert { host: String, percent: u8 },
Heartbeat { host: String },
}
fn summarize(event: &MetricEvent) {
match event {
// Both alternatives bind `host: &String` and `percent: &u8`:
MetricEvent::CpuAlert { host, percent }
| MetricEvent::MemAlert { host, percent } => {
println!("ALERT on {host}: usage at {percent}%");
}
MetricEvent::Heartbeat { host } => {
println!("OK heartbeat from {host}");
}
}
}
fn main() {
let e1 = MetricEvent::CpuAlert {
host: String::from("db-01"),
percent: 94,
};
let e2 = MetricEvent::Heartbeat {
host: String::from("web-02"),
};
summarize(&e1);
summarize(&e2);
}
ALERT on db-01: usage at 94%
OK heartbeat from web-02
Error: Missing Variable Binding in One Alternative (E0408)
// ❌ does not compile
enum Packet {
Data(u32),
Ping,
}
fn inspect(p: Packet) {
match p {
Packet::Data(id) | Packet::Ping => println!("{id}"),
}
}
fn main() {}
error[E0408]: variable `id` is not bound in all patterns
--> src/main.rs:8:28
|
8 | Packet::Data(id) | Packet::Ping => println!("{id}"),
| -- ^^^^^^^^^^^^ pattern doesn't bind `id`
| |
| variable not in all patterns
Operator Precedence of | with @ and Match Guards
- With
@bindings (x @ (1 | 2 | 3)): The@operator binds more tightly than|, so wrap the alternatives in parentheses (val @ (10 | 20 | 30)) if you wantvalto bind across all alternatives without repeatingval @ 10 | val @ 20. - With
ifmatch guards (A | B | C if condition): A match guardif conditionat the end of an arm applies to all alternativesA,B, andCin that arm.