Intermediate · Structs, enums and pattern matching · topic 20 of 50 · 30 min
Enums and Option
Explanation
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.
enum OrderStatus {
Placed,
Shipped,
Delivered,
Cancelled,
}
let s = OrderStatus::Shipped;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.
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;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:
enum Option<T> {
Some(T),
None,
}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}");
}
}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
| Method | What it does | Example result |
|---|---|---|
| unwrap_or(default) | The value, or a default if None | pin.unwrap_or(0) |
| map(f) | Change the value inside if Some | pin.map(|p| p + 1) |
| is_some() / is_none() | Check without taking the value | true or false |
| unwrap() | The value, or PANIC if None | Only when None is truly impossible |
| expect("msg") | Like unwrap, with your message | For 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
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.
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);
}Practice (0/3 done)
What does this print? let v = vec![10, 20, 30]; println!("{:?} {:?}", v.get(1), v.get(5));
Which enum correctly models a delivery that is either pending, or delivered with an OTP code?
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
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Pattern matching: match, if let and let elseStructs, enums and pattern matching · same module
- Result and the ? operatorError handling and collections
- Null safetyKotlin and Android App Development · Null safety, functions and lambdas
- std::optional, std::variant and std::string_viewC++ Programming (modern C++ and DSA) · Modern C++: lambdas, C++17 and C++20 essentials
- Data, enum and sealed classes, and objectsKotlin and Android App Development · Classes, objects and collections
- Unions, enums and memory layoutC Programming (from zero to systems) · Structs, unions, enums and files
Questions people ask about this
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
What does x ?: 0 return when x is null?Related
Which C++ standard introduced optional, variant and string_view?Related
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.
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 more topic in “Structs, enums and pattern matching” before its exam: Pattern matching: match, if let and let else.
- Module 5 exam: Structs, enums and pattern matching · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Error handling and collections. 50 topics left in the course before the final exam.