Skip to content

Enums

An enum defines a type that can be one of several named variants. In its simplest form it looks like a C enum:

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

Each variant is accessed with :: syntax. Derive Debug to print an enum with {:?}.

enum Shape {
Circle(f64), // radius
Rectangle(f64, f64), // width, height
Triangle(f64, f64, f64), // three sides
}

You construct a variant by calling it like a function:

let c = Shape::Circle(3.0);
let r = Shape::Rectangle(4.0, 5.0);

Variants can also hold named fields, just like a struct:

enum Message {
Move { x: i32, y: i32 },
Write(String),
Quit,
}

You can add methods to enums the same way you add them to structs — with an impl block and a match inside:

impl Shape {
fn name(&self) -> &str {
match self {
Shape::Circle(_) => "Circle",
Shape::Rectangle(_, _) => "Rectangle",
Shape::Triangle(_, _, _) => "Triangle",
}
}
}

Rust has no null. Instead, the standard library provides Option<T>:

enum Option<T> {
Some(T),
None,
}

Some(value) wraps a present value; None represents the absence of a value. The compiler forces you to handle both cases before you can use the inner value, eliminating null-pointer bugs at compile time.

#[derive(Debug)]
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle(f64, f64, f64),
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle(a, b, c) => {
let s = (a + b + c) / 2.0;
(s * (s - a) * (s - b) * (s - c)).sqrt()
}
}
}
fn name(&self) -> &str {
match self {
Shape::Circle(_) => "Circle",
Shape::Rectangle(_, _) => "Rectangle",
Shape::Triangle(_, _, _) => "Triangle",
}
}
}
fn main() {
let shapes: Vec<Shape> = vec![
Shape::Circle(3.0),
Shape::Rectangle(4.0, 5.0),
Shape::Triangle(3.0, 4.0, 5.0),
];
for shape in &shapes {
println!("{}: area = {:.2}", shape.name(), shape.area());
}
let maybe: Option<i32> = Some(42);
println!("Option: {:?}", maybe);
let none: Option<i32> = None;
println!("None: {:?}", none);
}
Which of these is a valid way to construct a data-carrying enum variant?
What does Rust's Option<T> replace compared to other languages?
Can you define methods on an enum in Rust?