Enums and Pattern Matching
The match Expression
The match expression compares a scrutinee value against an ordered list of patterns from top to bottom and evaluates the body expression of the first arm whose pattern matches.
Syntax and Evaluation Flow
match scrutinee {
Pattern1 => expression_1,
Pattern2 => {
// Multi-statement block arm (comma after `}` is optional)
expression_2
}
_ => fallback_expression,
}
Key properties of match:
- Top-to-bottom evaluation: Arms are tested in the exact order written; the first matching arm executes, and no subsequent arms run (there is no C-style fallthrough).
matchis an expression: It produces a value that can be assigned to aletbinding or returned from a function.- Type unification across arms: Every arm must evaluate to the same type (or diverge with
!, such asreturn,break,continue, orpanic!).
Matching Enums and Destructuring Variant Payloads
enum PaymentMethod {
Cash,
Card { last_four: u16, network: String },
WireTransfer(String),
}
fn format_receipt(method: &PaymentMethod, amount_cents: u64) -> String {
let dollars = amount_cents as f64 / 100.0;
match method {
PaymentMethod::Cash => format!("Paid ${dollars:.2} in cash"),
PaymentMethod::Card { last_four, network } => {
format!("Paid ${dollars:.2} via {network} ending in {last_four:04}")
}
PaymentMethod::WireTransfer(iban) => {
format!("Paid ${dollars:.2} by wire transfer ({iban})")
}
}
}
fn main() {
let p1 = PaymentMethod::Cash;
let p2 = PaymentMethod::Card {
last_four: 42,
network: String::from("Visa"),
};
let p3 = PaymentMethod::WireTransfer(String::from("DE89-3704-0044"));
println!("{}", format_receipt(&p1, 15_50));
println!("{}", format_receipt(&p2, 99_00));
println!("{}", format_receipt(&p3, 250_00));
}
Paid $15.50 in cash
Paid $99.00 via Visa ending in 0042
Paid $250.00 by wire transfer (DE89-3704-0044)
Matching Literals, Ranges, and Tuples
In addition to enums, match works on integers, booleans, char, &str, tuples, arrays, and slices:
fn classify_http_status(code: u16) -> &'static str {
match code {
200 => "OK",
201 | 202 | 204 => "Success (Created / Accepted / No Content)",
300..=399 => "Redirection",
400..=499 => "Client Error",
500..=599 => "Server Error",
_ => "Unknown Status Code",
}
}
fn main() {
for code in [200, 204, 301, 404, 503] {
println!("{code}: {}", classify_http_status(code));
}
}
200: OK
204: Success (Created / Accepted / No Content)
301: Redirection
404: Client Error
503: Server Error
Diverging Arms (!) Inside a match Expression
Because the never type ! coerces to any type T, an arm that returns early (return), breaks out of a loop (break), or panics (panic!) can coexist with arms that produce a concrete value:
fn parse_positive(input: &str) -> Result<u32, String> {
let num: u32 = match input.trim().parse() {
Ok(0) => return Err(String::from("zero is not positive")),
Ok(n) => n,
Err(e) => return Err(format!("invalid integer '{input}': {e}")),
};
Ok(num * 10)
}
fn main() {
println!("'7' -> {:?}", parse_positive("7"));
println!("'0' -> {:?}", parse_positive("0"));
println!("'abc' -> {:?}", parse_positive("abc"));
}
'7' -> Ok(70)
'0' -> Err("zero is not positive")
'abc' -> Err("invalid integer 'abc': invalid digit found in string")