title: Enums

An enum defines a type that can be one of several possible variants. Unlike enums in most languages, Rust enum variants can each hold their own data.

Basic enum

enum Direction {
    North,
    South,
    East,
    West,
}
 
let dir = Direction::North;

Variants with data

Each variant can hold different types and amounts of data:

enum Message {
    Quit,                       // no data
    Move { x: i32, y: i32 },   // named fields
    Write(String),              // single value
    ChangeColor(u8, u8, u8),    // multiple values
}

This is a key difference from most languages — a single enum type can represent several structurally different things.

Using match with enums

Enums and match go hand in hand. Match lets you handle each variant and extract its data:

fn handle(msg: Message) {
    match msg {
        Message::Quit => println!("quit"),
        Message::Move { x, y } => println!("move to {x}, {y}"),
        Message::Write(text) => println!("write: {text}"),
        Message::ChangeColor(r, g, b) => println!("color: {r}, {g}, {b}"),
    }
}

Methods on enums

Just like structs, enums can have methods:

impl Message {
    fn is_quit(&self) -> bool {
        matches!(self, Message::Quit)
    }
}

Enums vs structs

Use a struct when you always have all fields present. Use an enum when a value can be one of several distinct shapes. For example, a network packet could be a Connect, Data, or Disconnect — that’s a natural enum because you’ll never have all three at once.

Option and Result

Rust’s two most important enums are built into the standard library:

  • Option<T> — a value that may or may not exist (see [[option]])
  • Result<T, E> — a value that is either a success or an error (see [[result]])

Both follow the same pattern: an enum whose variants carry data, used with match to handle each case.