Skip to content
DocsRust LearningintermediateEnums & Pattern Matching
Chapter 7 of 19·intermediate·8 min read

Enums & Pattern Matching

Enum & Khớp Mẫu

Enums, Option, Result, and match

Hover or tap any paragraph to see Vietnamese translation

Defining Enums

An enum (short for enumeration) lets you define a type by listing its possible variants. Each variant is a valid value of that type.

direction.rs
1enum Direction {2    North,3    South,4    East,5    West,6}78fn main() {9    let dir = Direction::North;1011    match dir {12        Direction::North => println!("Heading north"),13        Direction::South => println!("Heading south"),14        Direction::East  => println!("Heading east"),15        Direction::West  => println!("Heading west"),16    }17}

Enum variants are accessed with the double-colon (::) notation. Enums carry no data by default but are still first-class types in Rust's type system.

Info
Unlike C or C++, Rust enum variants are not integers by default. They are fully named, type-safe variants.

Enums with Data (Variants with Values)

The real power of Rust enums is that each variant can carry its own data — like a typed union or a sum type in type theory.

shapes.rs
1enum Shape {2    Circle(f64),                  // radius3    Rectangle(f64, f64),          // width, height4    Triangle { base: f64, height: f64 }, // named fields5}67fn area(shape: &Shape) -> f64 {8    match shape {9        Shape::Circle(r) => std::f64::consts::PI * r * r,10        Shape::Rectangle(w, h) => w * h,11        Shape::Triangle { base, height } => 0.5 * base * height,12    }

Variants can hold anonymous tuples, named struct fields, or nothing at all. This makes Rust enums far more expressive than enums in most other languages.

The Option<T> Enum

Rust has no null. Instead it uses the standard library's Option<T> type to represent a value that may or may not be present. This forces you to handle absence explicitly.

option.rs
1// Option is defined in std as:2// enum Option<T> {3//     Some(T),4//     None,5// }67fn divide(a: f64, b: f64) -> Option<f64> {8    if b == 0.0 {9        None10    } else {11        Some(a / b)12    }
Tip
Some and None are in the prelude, so you write Some(...) and None directly without the Option:: prefix.

The match Expression (Exhaustive Matching)

The match expression compares a value against a series of patterns and executes the first arm that matches. Rust's compiler requires match to be exhaustive — you must cover every possible variant.

coin.rs
1#[derive(Debug)]2enum Coin {3    Penny,4    Nickel,5    Dime,6    Quarter(String), // State quarter with state name7}89fn value_in_cents(coin: &Coin) -> u32 {10    match coin {11        Coin::Penny => {12            println!("Lucky penny!");

Each match arm has the form pattern => expression. If the body is multiple statements, wrap it in braces. The value of the matched arm becomes the value of the entire match expression.

Patterns in match Arms

Rust supports many powerful pattern forms inside match expressions: numeric ranges, multiple patterns at once, @ bindings, and guard conditions.

patterns.rs
1fn classify(n: i32) -> &'static str {2    match n {3        i32::MIN..=-1 => "negative",4        0 => "zero",5        1..=9 => "single digit",6        10..=99 => "double digit",7        _ => "large",8    }9}1011fn describe_pair(pair: (i32, i32)) -> String {12    match pair {

The Wildcard Pattern _

The _ pattern matches any value without binding it to a variable. It is commonly used as the final catch-all arm in a match to satisfy exhaustiveness.

if let and while let

Sometimes you only care about one variant. The if let syntax lets you match one pattern and ignore all other cases without a full match block.

if_let.rs
1fn main() {2    let config_max = Some(3u8);34    // Verbose match5    match config_max {6        Some(max) => println!("Max is {max}"),7        None => (),8    }910    // Equivalent, more concise if let11    if let Some(max) = config_max {12        println!("Max is {max}");
Tip
if let sacrifices the exhaustiveness you get from match. Use it when you genuinely only care about one case; use match when your logic depends on multiple variants.

Nested Enums

Enum variants can contain other enums, creating complex, structured data types that accurately reflect your domain model.

nested_enums.rs
1#[derive(Debug)]2enum Color {3    Rgb(u8, u8, u8),4    Named(NamedColor),5}67#[derive(Debug)]8enum NamedColor {9    Red,10    Green,11    Blue,12    Custom(String),

Common Enum Patterns in Rust

Enums appear everywhere in Rust code. Here are the most common patterns you will encounter and use daily.

State Representation

state_machine.rs
1#[derive(Debug, PartialEq)]2enum TrafficLight {3    Red,4    Yellow,5    Green,6}78impl TrafficLight {9    fn next(&self) -> Self {10        match self {11            TrafficLight::Red => TrafficLight::Green,12            TrafficLight::Green => TrafficLight::Yellow,

Message / Event Types

events.rs
1#[derive(Debug)]2enum AppEvent {3    KeyPress(char),4    MouseClick { x: i32, y: i32 },5    Resize(u32, u32),6    Quit,7}89fn handle(event: AppEvent) {10    match event {11        AppEvent::KeyPress(c) => println!("Key: {c}"),12        AppEvent::MouseClick { x, y } => println!("Click at ({x}, {y})"),
  • Use enums when a value can be one of several distinct variants
  • Use match for exhaustive handling of all possible variants
  • Use if let when you only care about a single variant
  • Avoid panicking unwraps on Option — match or use unwrap_or instead
  • Implement methods on enums to encapsulate related behavior

Key Takeaways

Điểm Chính

  • Enums define types with multiple named variantsEnum định nghĩa kiểu với nhiều biến thể có tên
  • Option<T> replaces null with Some(value) and NoneOption<T> thay thế null bằng Some(giá trị) và None
  • match expressions must handle every possible variantBiểu thức match phải xử lý mọi biến thể có thể
  • if let is a shorthand for matching a single patternif let là cách viết tắt để khớp một mẫu duy nhất

Practice

Test your understanding of this chapter

Quiz

What is the correct syntax to access an enum variant in Rust?

Cú pháp đúng để truy cập một biến thể enum trong Rust là gì?

Quiz

What does the None variant of Option<T> represent?

Biến thể None của Option<T> đại diện cho điều gì?

True or False

Rust's match expression must be exhaustive — it must cover every possible variant of the matched type.

Biểu thức match trong Rust phải toàn diện — nó phải bao phủ mọi biến thể có thể có của kiểu được khớp.

True or False

Rust enum variants are integers by default, just like in C or C++.

Các biến thể enum trong Rust là số nguyên theo mặc định, giống như trong C hay C++.

Code Challenge

Extract a value from Option using if let

Trích xuất giá trị từ Option bằng if let

let x: Option<i32> = Some(42);
if let (value) = x {
    println!("{value}");
}

Chapter Complete!

Great job! Keep the momentum going.

Your progress0 of 19 chapters read
← → to navigate chapters
Built: 4/8/2026, 12:01:11 PM