Chapter 6

Enums and Pattern Matching

👋 Anyone can read and edit this exercise. Sign up to save your progress.

“Dad, why is my sister’s name Rose?”
“Because your mother loves roses.”
“Thanks, dad!”
“No problem, Rust enums.”

Dad is right, enums are the best. If you know the crippled form of enums in other languages (cough C), I'm so sorry for you. In Rust, they are a pure delight to work with.

But first things first: an enum is a type whose value is one of a fixed set of variants. Think of it as a "this or that or that" type.

enum HttpStatus {
    Ok,
    NotFound,
    InternalServerError,
}

You typically inspect an enum value with match. The compiler insists you handle every variant, which is one of Rust's most loved features: when you add a new variant later, every match that didn't account for it stops compiling and tells you exactly where to update.

fn code(status: HttpStatus) -> u16 {
    match status {
        HttpStatus::Ok => 200,
        HttpStatus::NotFound => 404,
        HttpStatus::InternalServerError => 500,
    }
}

Each arm of a match is pattern => expression. Multiple patterns can share an arm with |, and the catch-all is _:

match code {
    200 | 201 | 204 => "success",
    404 => "missing",
    _ => "something else",
}

#[derive(...)]: free implementations

You'll see this line on many types in Rust:

#[derive(Debug, PartialEq)]
enum HttpStatus {
    Ok,
    NotFound,
    InternalServerError,
}

The #[...] syntax is an attribute: extra instructions for the compiler attached to the item below. derive is the most common one. It says "please write the boilerplate for these capabilities for me." Each name inside the parentheses is a trait (Rust's name for a shared interface, similar to a Java interface or a Haskell type class; traits get their own chapter later).

The two we use right away:

Derive works on enums and structs whose fields all implement the same traits. The compiler writes the obvious implementation. For PartialEq on an enum, that means "two values are equal if they're the same variant with equal payloads." You can always write the implementation by hand instead when you need different behaviour.

Mapping variants to values

Your first match: turn each HttpStatus variant into the numeric code it represents. The compiler will complain if you forget a variant, which is exactly what you want.

Useful from the standard library

  • The Rust Book on match is the reference for pattern syntax: literal patterns, | for multiple patterns, ranges, and the _ catch-all.
  • std::cmp::PartialEq is the trait that lets you use == on a value. #[derive(PartialEq)] on the enum asks the compiler to write the implementation for you, which is what makes assert_eq! in the tests work.
  • std::fmt::Debug enables {:?} formatting. Handy when a test fails and you want to see which variant came back.
Exercise 1 of 2
Open in Web Editor

Results

    Compiler / runtime output
    
                

    Matching one variant

    Sometimes you only care about a single variant. You can still write a full match with a _ catch-all arm, or you can reach for the matches! macro. Both are idiomatic.

    Here, only a server-side failure (InternalServerError) is worth retrying. Client errors like NotFound or BadRequest mean the request itself is wrong, so retrying won't help.

    Useful from the standard library

    • std::matches! expands to a match that returns true for the given pattern and false otherwise. Reads naturally as matches!(status, HttpStatus::InternalServerError).
    • The == operator works on enums that derive PartialEq, so status == HttpStatus::InternalServerError is equally fine. Pick whichever reads better at the call site.
    Exercise 2 of 2
    Open in Web Editor

    Results

      Compiler / runtime output
      
                  

      Wrapping up enums and pattern matching

      You defined an enum with a fixed set of variants, mapped each variant to a value with a match, and used matches! to ask a yes/no question about a single variant.

      What we learned

      • An enum is a "this or that or that" type. Each value is exactly one of its variants, and the compiler tracks which one.
      • match checks a value against patterns top to bottom and runs the first arm that fits. Every arm is pattern => expression, and the whole match is itself an expression that produces a value.
      • match is exhaustive: leave a variant unhandled and the compiler refuses to build. Add a new variant later and every match that needs updating tells you exactly where.
      • | lets multiple patterns share an arm (200 | 201 | 204 => ...), and _ is the catch-all when you want to ignore the rest.
      • #[derive(Debug, PartialEq)] is the usual pair on a plain enum: one for {:?} printing, one for ==. Add Clone, Copy when the variants carry no heap data so values can be passed around freely.
      • For a single-variant check, matches!(value, Variant) is the compact form; value == Variant works equally well when PartialEq is derived.
      Next chapter 7Vectors