Rust The Option Type
In many programming languages, a variable can hold a special value called null or nil to represent "nothing." This causes endless bugs — you try to use the value, forget it might be null, and the program crashes. Rust does not have null. Instead, it uses the Option type to represent values that might or might not exist.
What Is Option?
Option<T> is an enum built into Rust's standard library. It has two variants:
enum Option<T> {
Some(T), ← Contains a value of type T
None, ← Contains nothing
}
The T is a placeholder for any type. Option<i32> means "either an integer or nothing." Option<String> means "either a string or nothing."
The Gift Box Diagram
Some(42) = [ Box with 42 inside ] ← You get something None = [ Empty box ] ← You get nothing You always receive a box. The question is whether there is anything inside.
Creating Option Values
let age: Option<i32> = Some(25); let missing: Option<i32> = None;
You do not need to write Option::Some or Option::None because they are imported automatically. Just write Some(value) or None.
Using Option with match
To use the value inside an Option, you must handle both cases — the Some case and the None case. match forces you to deal with both:
fn print_age(age: Option<i32>) {
match age {
Some(a) => println!("Age is {}.", a),
None => println!("Age is unknown."),
}
}
fn main() {
print_age(Some(30));
print_age(None);
}
Output:
Age is 30. Age is unknown.
A Real-World Example
Finding an item in a collection returns an Option because the item might not be there:
fn find_score(name: &str) -> Option<u32> {
if name == "Alice" {
Some(95)
} else {
None
}
}
fn main() {
let result = find_score("Alice");
match result {
Some(score) => println!("Alice scored {}.", score),
None => println!("Alice not found."),
}
}
Output:
Alice scored 95.
Helpful Option Methods
Rust gives you convenience methods so you do not always need a full match expression.
unwrap()
Extracts the value from Some. Panics if the value is None. Use only when you are certain the value exists:
let x: Option<i32> = Some(10); let value = x.unwrap(); ← value is 10 let y: Option<i32> = None; let bad = y.unwrap(); ← Panic: called unwrap on None
unwrap_or(default)
Returns the value if Some, or a default value if None. This is the safest common alternative to unwrap:
let score = find_score("Bob").unwrap_or(0);
println!("Bob's score: {}", score); ← 0 (Bob not found)
is_some() and is_none()
Check whether an Option holds a value or not:
let x: Option<i32> = Some(5);
println!("{}", x.is_some()); ← true
println!("{}", x.is_none()); ← false
if let with Option
When you only care about the Some case, use if let:
let maybe_name: Option<&str> = Some("Alice");
if let Some(name) = maybe_name {
println!("Hello, {}!", name);
}
map() — Transform the Inner Value
Apply a function to the value inside Some and return a new Option. If the original is None, map passes None through without running the function:
let score: Option<u32> = Some(80);
let doubled = score.map(|s| s * 2);
println!("{:?}", doubled); ← Some(160)
let no_score: Option<u32> = None;
let still_none = no_score.map(|s| s * 2);
println!("{:?}", still_none); ← None
Option vs Null
Language "No value" concept Can you forget to check? -------- ------------------ ------------------------ Java/C# null Yes — NullPointerException at runtime Python None Yes — AttributeError at runtime Rust Option<T> No — compiler forces you to handle None
Quick Reference
Option<T> ← Type that is Some(T) or None
Some(value) ← Has a value
None ← Has no value
opt.unwrap() ← Get the value (panics on None)
opt.unwrap_or(default) ← Get the value or a fallback
opt.is_some() ← True if Some
opt.map(|v| v + 1) ← Transform the inner value
if let Some(x) = opt { } ← Handle the Some case only
