Exerpad
🔥1
10 XP
Lv.1 Beginner
Log In

Enums and Match

Sometimes a value can only be one of a few options. Think about it: a traffic light is either Red, Yellow, or Green. A coin is either a Penny, Nickel, Dime, or Quarter. A direction is either Up, Down, Left, or Right.

In Rust, we use enums to define types like these. And we use match to do something different for each option. Together, they're one of Rust's superpowers!

Defining an Enum

An enum (short for "enumeration") lists all the possible values a type can have:

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

fn main() {
let go = Direction::Up;

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

Each option inside the enum is called a variant. You create one by writing the enum name, then ::, then the variant: Direction::Up.

The Power of match

The match expression checks which variant you have and runs the matching code. It's like a super-powered if/else if chain!

One important rule: match must be exhaustive. That means you must handle every variant. If you forget one, Rust won't compile your code. This keeps you safe from bugs!

rust
enum Animal {
Cat,
Dog,
Fish,
}

fn main() {
let pet = Animal::Cat;

match pet {
Animal::Cat => println!("Meow!"),
Animal::Dog => println!("Woof!"),
Animal::Fish => println!("Blub blub!"),
}
}

Try removing one of the lines inside match and see what error Rust gives you!

The _ Wildcard

Sometimes you only care about one or two variants and want to handle the rest the same way. Use _ as a catch-all:

rust
enum Planet {
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Uranus,
Neptune,
}

fn main() {
let home = Planet::Earth;

match home {
Planet::Earth => println!("Home sweet home!"),
_ => println!("Not our home planet."),
}
}

The _ matches anything that wasn't matched above. It must come last in the match.

Enums with Data

Here's where enums get really cool. Variants can carry data inside them!

rust
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}

fn main() {
let s = Shape::Circle(5.0);

match s {
Shape::Circle(radius) => {
let area = 3.14159 * radius * radius;
println!("Circle area: {}", area);
}
Shape::Rectangle(width, height) => {
let area = width * height;
println!("Rectangle area: {}", area);
}
}
}

Circle holds one f64 (the radius), and Rectangle holds two (width and height). When you match, you can unpack that data into variables.

Option<T> — Rust's Way of Saying "Maybe"

Rust has a built-in enum called Option that represents a value that might or might not exist:

rust
// Option is defined like this (you don't need to write this yourself):
// enum Option<T> {
// Some(T),
// None,
// }

fn main() {
let numbers = vec![10, 20, 30];
let first = numbers.get(0);
let missing = numbers.get(99);

match first {
Some(value) => println!("Found: {}", value),
None => println!("Nothing there!"),
}

match missing {
Some(value) => println!("Found: {}", value),
None => println!("Nothing there!"),
}
}

Instead of crashing when something is missing, Rust wraps it in Option. You get Some(value) when it exists, or None when it doesn't. The match forces you to handle both cases!

if let — Quick Pattern Match

When you only care about one pattern, if let is a handy shortcut:

rust
fn main() {
let favorite: Option<&str> = Some("pizza");

if let Some(food) = favorite {
println!("I love {}!", food);
} else {
println!("I have no favorite.");
}
}

This is the same as a match with two arms, but shorter when you only need to check for one pattern.

Now let's practice defining enums and matching on them!