rust Apr 22, 2024
Exhaustive Pattern Matching in Rust
Using Rust's match expressions and enums to handle every variant at compile time — no missing cases allowed.
Rust’s match is exhaustive: the compiler enforces that every possible variant is handled. Add a new variant and every match expression that forgets it becomes a compile error.
Modeling states with enums
connection.rs
#[derive(Debug)]
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { latency_ms: u64 },
Error { message: String },
}
fn describe(state: &ConnectionState) -> String {
match state {
ConnectionState::Disconnected => {
"Not connected".to_string()
}
ConnectionState::Connecting { attempt } => {
format!("Connecting (attempt #{})", attempt)
}
ConnectionState::Connected { latency_ms } => {
format!("Connected ({}ms latency)", latency_ms)
}
ConnectionState::Error { message } => {
format!("Error: {}", message)
}
}
} Why this matters
If you add a fifth variant like ConnectionState::Reconnecting { delay_ms: u64 }, the compiler will flag every match that doesn’t handle it. This turns runtime bugs into compile-time errors.
Guards and bindings
guards.rs
fn severity(state: &ConnectionState) -> &str {
match state {
ConnectionState::Connected { latency_ms } if *latency_ms > 500 => "warn",
ConnectionState::Connected { .. } => "ok",
ConnectionState::Error { .. } => "critical",
_ => "info",
}
} Guards add conditions to individual arms without losing exhaustiveness guarantees.