thecodex.expert · The Codex Family of Knowledge
Rust

Enums and Pattern Matching

Rust has no null — the absence of a value is represented by Option, an ordinary enum, and match forces every case to be handled at compile time.

Rust 1.96 match is exhaustive Last verified:
Canonical Definition

An enum defines a type with a fixed set of named variants, and unlike C-style enums, each Rust variant can carry its own associated data of any shape. match compares a value against a series of patterns and runs the code for the first match; critically, the compiler requires every possible variant to be handled (or an explicit _ catch-all), so a newly added enum variant that isn't handled somewhere is a compile error, not a silently-skipped runtime bug.

Enums with data per variant

Each variant can carry different data — a fundamentally more expressive tool than C's plain-integer enums.

Rustenums.rs
enum Message {
    Quit,                       // no data
    Move { x: i32, y: i32 },     // named fields, like a struct
    Write(String),               // one value
    ChangeColor(i32, i32, i32),  // a tuple of values
}

let msg = Message::Move { x: 10, y: 20 };

Option<T>: Rust's answer to null

Rust has no null value. Instead, the standard library defines Option<T> as an ordinary enum with two variants: Some(T) holds a value, None holds nothing. Because it's a distinct type from T, the compiler forces you to explicitly handle the "nothing" case before you can use the inner value — there's no way to accidentally dereference a null pointer.

Rustoption.rs
// The actual standard library definition:
// enum Option<T> { Some(T), None }

fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some(String::from("Alice"))
    } else {
        None
    }
}

match find_user(1) {
    Some(name) => println!("Found: {}", name),
    None => println!("Not found"),
}

match must be exhaustive; if let for the simple case

If a match doesn't cover every variant, it's a compile error — this is what makes match a safety feature, not just a switch statement. For the common case of only caring about one variant, if let is a shorter alternative to a full match.

Rustif_let.rs
let config_value: Option<u8> = Some(3);

// full match: handles both cases
match config_value {
    Some(v) => println!("value is {}", v),
    None => println!("no value"),
}

// if let: shorter, only cares about the Some case
if let Some(v) = config_value {
    println!("value is {}", v);
}

Sources

1
The Rust Project. The Rust Programming Language, "Enums and Pattern Matching", doc.rust-lang.org/book/ch06.