---
title: Enums and Pattern Matching
url: https://doc.liz6.com/en/rust/02-type-system/02-enums-and-pattern-matching
locale: en
area: rust
tags:
- rust
- type-system
date: 2026-06-30
modified: 2026-07-16
description: Rust’s enums are tagged unions—each variant carries its own data, and the compiler ensures that match expressions cover all variants (exhaustiveness checking). Option and Result are not built into the language; they are defined in the standard library using enums—demonstrating that enums are Rust’s most expressive type constructor.
---

# Enums and Pattern Matching

> Rust’s enums are tagged unions—each variant carries its own data, and the compiler ensures that match expressions cover all variants (exhaustiveness checking). Option and Result are not built into the language; they are defined in the standard library using enums—demonstrating that enums are Rust’s most expressive type constructor.

## Enums are Tagged Unions, Not Just "Enumerations"

C’s enums are merely a set of named integer constants. In Rust, each variant of an enum can carry different data—it is a tagged union:

```rust
enum Message {
    Quit,                            // No data — pure tag
    Move { x: i32, y: i32 },         // Anonymous struct
    Write(String),                   // Tuple variant
    ChangeColor(u8, u8, u8),         // Tuple of 3 u8s
}
```

The compiler allocates enough space for `Message` to hold the largest variant (`Move` = two i32s = 8 bytes), plus a discriminant (tag) field. For cases with a niche, such as `Option<&T>`, the tag can be optimized away.

## Option: Rust Has No Null

Tony Hoare called null the "billion-dollar mistake." Rust has no null—values that might not exist are represented by `Option<T>`:

```rust
fn safe_div(a: i32, b: i32) -> Option<i32> {
    if b == 0 { None } else { Some(a / b) }
}
// Callers must handle None:
match safe_div(10, 0) {
    Some(result) => println!("{}", result),
    None => println!("division by zero"),
}
```

The compiler forces you to handle the `None` case—you cannot forget to check. `Option<T>` is an ordinary type, not a special null marker, so you can use combinators like `map` and `and_then` on `Option`.

## Result: Errors Are Values

The problem with exceptions is that they break control flow—you cannot tell from a function signature what exceptions it might throw. `Result<T, E>` makes errors part of the return value:

```rust
fn parse(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse()                        // Returns Result, does not throw
}
```

When callers see `Result<i32, ParseIntError>`, they know that this function may successfully return an `i32` or fail with a parse error. There is no hidden control flow.

## Match: Exhaustiveness Checking at Compile Time

```rust
match number {
    1 => "one",
    2 => "two",
    // _ => "other",                 // If this line is missing, compilation fails:
                                     // "non-exhaustive patterns"
}
```

The compiler checks whether the match covers all possible variants. If a new variant is added to the type (e.g., a new member to an enum), every match expression involving that type will fail to compile—forcing you to explicitly handle the new case.

## Various Forms of Patterns

```rust
// Destructuring
let (x, y, z) = (1, 2, 3);
let Point { x, y } = point;

// ref pattern: does not move, borrows instead
let ref r = value;                   // r: &T, value is not moved

// Match guard: additional condition
match num {
    n if n % 2 == 0 => "even",
    n if n > 10 => "big",
    _ => "other",
}

// @ binding: matches the pattern and binds the variable simultaneously
match opt {
    Some(v @ 1..=5) => println!("small: {}", v),
    Some(v) => println!("large: {}", v),
    None => (),
}

// if let / while let: syntactic sugar for when you only care about one variant
if let Some(v) = opt { process(v); }
while let Some(v) = iter.next() { process(v); }
```

## References

- **Rust Book**: Chapter 6 (Enums), Chapter 18 (Patterns)
- **Rust Reference**: Match expressions, Patterns

*Keywords: enum, Option, Result, match, pattern, if let, while let, exhaustiveness, tagged union*
