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:

  1. 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).
  2. match is an expression: It produces a value that can be assigned to a let binding or returned from a function.
  3. Type unification across arms: Every arm must evaluate to the same type (or diverge with !, such as return, break, continue, or panic!).

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")