`Option<T>`
EssentialPrerequisites
Option<T> is Rust’s replacement for null. Instead of allowing any reference to be null and crashing at runtime, Rust makes the absence of a value explicit in the type: a function returning Option<String> either returns Some(String) with a value or None with nothing. The compiler forces you to handle both.
The definition
Option<T> is defined in the standard library as:
enum Option<T> {
None,
Some(T),
}
None and Some are in scope automatically — you do not need to write Option::None or Option::Some, though you may.
Matching on an Option
The canonical way to inspect an Option is pattern matching:
fn greet(name: Option<&str>) {
match name {
Some(n) => println!("Hello, {n}!"),
None => println!("Hello, stranger!"),
}
}
match is exhaustive: the compiler refuses to compile the code if either arm is missing.
For the pattern of “do something only when Some”, if let is more concise:
if let Some(n) = name {
println!("Hello, {n}!");
}
The ? operator
Inside a function that returns Option<T>, the ? operator short-circuits on None: it unwraps a Some or immediately returns None from the surrounding function.
fn double_first(numbers: &[i32]) -> Option<i32> {
let first = numbers.first()?; // None if the slice is empty
first.checked_mul(2) // None if the result would overflow
}
? on an Option is sugar for:
match value {
Some(x) => x,
None => return None,
}
Combinators
Option provides higher-order methods for transforming and composing optional values without explicit match.
map transforms the inner value if present:
let n: Option<i32> = Some(4);
let doubled = n.map(|x| x * 2); // Some(8)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2); // None
and_then chains an operation that itself returns an Option:
fn parse_and_double(s: &str) -> Option<i32> {
s.parse::<i32>().ok().and_then(|n| n.checked_mul(2))
}
and_then is used when map would produce Option<Option<T>> — it flattens the result automatically.
unwrap_or provides a fallback value:
let value = optional.unwrap_or(0);
unwrap_or_else computes the fallback lazily, avoiding the cost when Some:
let value = optional.unwrap_or_else(|| expensive_default());
Converting between Option and Result
Option and Result are close cousins. ok_or attaches an error to turn None into Err:
let value: Result<i32, &str> = Some(42).ok_or("missing"); // Ok(42)
let error: Result<i32, &str> = None.ok_or("missing"); // Err("missing")
In the other direction, Result::ok() discards the error and returns an Option:
let opt: Option<i32> = "42".parse::<i32>().ok(); // Some(42)
The full Result<T, E> type is covered in Result.
Summary
Option<T>encodes “present” asSome(T)and “absent” asNone— no null pointers.matchandif letdestructureOptionwith exhaustiveness checking.?in anOption-returning function short-circuits toNoneautomatically.maptransforms the inner value;and_thenchains optional operations;unwrap_orprovides a default.ok_orconvertsOptiontoResult;Result::ok()converts back.