Enums: Defining Variant Types in Rust

An enum in Rust is a type that can represent one of several possible variants, each optionally holding different types of data. Enums are ideal for modeling choices, states, or patterns like Option, Result, and more.

If structs in Rust let you group fields together, enums let you define a type that can be only one of several named possibilities. This is Rust’s way of expressing things like “a value is either this or that.”

In this post, I will show you how to define enums, use them in pattern matching, and combine them with data. Enums are central to how Rust handles options, errors, and logic branches safely.

What is an Enum?

An enum lets you define a type by listing its possible variants.

Example

enum Direction {
    Up,
    Down,
    Left,
    Right,
}

This means a value of type Direction can be Up, Down, Left, or Right, but never more than one at the same time.

Creating and Using Enums

To use an enum, create a value with one of its variants:

let dir = Direction::Left;

You refer to a variant using :: syntax.

Pattern Matching with Enums

You usually match on enums using match:

match dir {
    Direction::Up => println!("Going up"),
    Direction::Down => println!("Going down"),
    Direction::Left => println!("Turning left"),
    Direction::Right => println!("Turning right"),
}

Rust forces you to handle every possible case, which makes enum usage safe and predictable.

Enums with Data

Variants can also hold data:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Text(String),
    ChangeColor(u8, u8, u8),
}

Each variant has its own structure:

let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Text(String::from("Hello"));
let m4 = Message::ChangeColor(255, 255, 0);

This is more flexible than a struct because each variant can store different kinds of values.

Matching Enums with Data

Use match to destructure the values:

match m2 {
    Message::Move { x, y } => println!("Move to {}, {}", x, y),
    Message::Quit => println!("Quit"),
    Message::Text(s) => println!("Message: {}", s),
    Message::ChangeColor(r, g, b) => println!("RGB: {}, {}, {}", r, g, b),
}

This works just like match on basic enums, but with structured data.

Enum Methods

Just like structs, enums can have methods in an impl block:

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("Goodbye"),
            Message::Text(s) => println!("Text: {}", s),
            _ => println!("Other message"),
        }
    }
}

You can then call:

let msg = Message::Text(String::from("Hi"));
msg.call();

Enums in the Standard Library

Rust uses enums all over the place. Two of the most important are:

  • Option – for values that may or may not exist
  • Result – for operations that may fail

You will learn more about these in upcoming lessons.

Summary

Enums let you define a type that can take on multiple structured forms. You use them with match to handle all possible cases safely. They are powerful, readable, and core to idiomatic Rust.