Skip to content

Intermediate · Structs, enums and pattern matching · topic 20 of 50 · 30 min

Enums and Option

Explanation

An enum is a type whose value is exactly one of a fixed set of variants, and each variant can carry its own data. Rust has no null; instead, a value that may be missing has the type Option<T>, which is either Some(value) or None. The compiler forces you to handle None before you can use the value.

Why enums?

Some values are "one of a few kinds". A payment is UPI, card or cash. An order is placed, shipped, delivered or cancelled. A struct says "this AND that"; an enum (short for enumeration) says "this OR that". Each possible kind is called a variant.

rust
enum OrderStatus {
    Placed,
    Shipped,
    Delivered,
    Cancelled,
}

let s = OrderStatus::Shipped;
A value of type OrderStatus is exactly one of the four variants.

Variants that carry data

In Rust, each variant can hold different data. This is much more powerful than enums in C or Java, where variants are just named numbers.

rust
enum Payment {
    Upi(String),                    // holds a UPI ID
    Card { last4: u16, network: String },
    Cash,
}

let p1 = Payment::Upi(String::from("asha@okbank"));
let p2 = Payment::Card { last4: 4242, network: String::from("RuPay") };
let p3 = Payment::Cash;
One type, three shapes. You cannot accidentally read a card number from a cash payment.

Enums can have methods too, in an impl block, just like structs. You will see in the next topic how match reads the data out of each variant.

The billion-dollar mistake: null

Many languages have null, a special value meaning "nothing here". Any variable might secretly be null, and using it crashes the program (NullPointerException in Java, "cannot read properties of null" in JavaScript). Tony Hoare, who introduced null in 1965, later called it his "billion-dollar mistake".

Option<T>

Rust has no null. When a value might be missing, its type says so: Option<T>. It is a normal enum from the standard library:

rust
enum Option<T> {
    Some(T),
    None,
}
T is a placeholder for any type (a generic, module 7). Option<u32> is "maybe a u32".
rust
fn find_pincode(city: &str) -> Option<u32> {
    match city {
        "Jaipur" => Some(302001),
        "Patna" => Some(800001),
        _ => None,
    }
}

fn main() {
    let pin = find_pincode("Jaipur");
    // let next = pin + 1;   // error: cannot add an integer to Option<u32>
    if let Some(p) = pin {
        println!("PIN {p}");
    }
}
You cannot use an Option<u32> as a u32. You must first check whether it is Some.

This is the key benefit. A plain u32 is always a real number. Only a value whose type is Option can be missing, and the compiler makes you handle that case. Missing values can no longer surprise you.

Useful Option methods

MethodWhat it doesExample result
unwrap_or(default)The value, or a default if Nonepin.unwrap_or(0)
map(f)Change the value inside if Somepin.map(|p| p + 1)
is_some() / is_none()Check without taking the valuetrue or false
unwrap()The value, or PANIC if NoneOnly when None is truly impossible
expect("msg")Like unwrap, with your messageFor prototypes and tests

Many standard functions return Option: v.get(i) on a Vec, s.find('x') on a string, iter.max(), map.get(&key). Whenever something "might not be there", look for an Option.

Common mistakes

  • Calling unwrap() everywhere. Each one is a possible crash. Use match, if let or unwrap_or.
  • Writing Some for values that can never be missing. Use the plain type; Option only where absence is real.
  • Comparing with null or using 0 as "missing". Use None.
  • Forgetting the enum name: Shipped instead of OrderStatus::Shipped (unless you import the variants with use).

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Enums and Option · 1/6

enum = one of several kinds

A struct means this and that. An enum means this or that: exactly one of a fixed set of variants.

Prefer a video by a teacher? Find free videos on “rust enums option some none explained” on YouTube ↗

In real life: An optional alternate phone number

A hospital registration form has a required mobile number and an optional alternate number. In the patient struct, alt_phone has the type Option<String>. When the hospital sends an SMS, the code must handle None explicitly, so it never tries to text an empty or missing number, and a reader of the struct sees at once which fields may be blank.

rust
struct Patient {
    name: String,
    phone: String,
    alt_phone: Option<String>,
}

fn main() {
    let p = Patient { name: String::from("Lakshmi"), phone: String::from("9876500000"), alt_phone: None };
    let backup = p.alt_phone.as_deref().unwrap_or("not given");
    println!("{}: {} (alt: {})", p.name, p.phone, backup);
}
as_deref turns Option<String> into Option<&str>. Prints: Lakshmi: 9876500000 (alt: not given)

Practice (0/3 done)

Task 1 · Question

What does this print? let v = vec![10, 20, 30]; println!("{:?} {:?}", v.get(1), v.get(5));

Task 2 · Question

Which enum correctly models a delivery that is either pending, or delivered with an OTP code?

Task 3 · Practice

Define enum Shape { Circle(f64), Rect { w: f64, h: f64 }, Square(f64) } and give it a method area(&self) -> f64 (use match). Then write fn find_seat(booked: &[u32], seat: u32) -> Option<usize> that returns the position of a seat number, and print the result for a booked and an unbooked seat using unwrap_or.

Quick quiz

Question 1

1.What is Option<T>?

Question 2

2.Why is Option safer than null?

Question 3

3.What does Some(5).unwrap_or(0) give? And None.unwrap_or(0)?

Question 4

4.What can an enum variant hold in Rust?

Question 5

5.Which is the risk of calling unwrap() on an Option?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is Option<T>?

Answer: An enum with Some(T) and None

Option is a normal enum from the standard library.

Why is Option safer than null?

Answer: Only Option types can be missing, and the compiler makes you handle None

Absence is visible in the type and must be handled.

What does Some(5).unwrap_or(0) give? And None.unwrap_or(0)?

Answer: 5 and 0

unwrap_or returns the inner value, or the default for None.

What can an enum variant hold in Rust?

Answer: Any data: none, a tuple of values, or named fields

Rust variants can be unit-like, tuple-like or struct-like.

Which is the risk of calling unwrap() on an Option?

Answer: It panics if the value is None

unwrap crashes the program on None.

What are the two variants of Result?Related

Answer: Ok and Err

Result<T, E> is either Ok(T) or Err(E).

From Result and the ? operator

What does x ?: 0 return when x is null?Related

Answer: 0

Elvis returns the right side when the left is null.

From Null safety

Which C++ standard introduced optional, variant and string_view?Related

Answer: C++17

All three came in C++17.

From std::optional, std::variant and std::string_view

Why can a when over a sealed type skip else?Related

Answer: The compiler knows every subtype and checks all are handled

Exhaustiveness is checked at compile time.

From Data, enum and sealed classes, and objects

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Structs, enums and pattern matching” before its exam: Pattern matching: match, if let and let else.
  2. Module 5 exam: Structs, enums and pattern matching · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Error handling and collections. 50 topics left in the course before the final exam.

← Previous: Methods and associated functions