Rust Option Explained: Some, None, if let, unwrap_or โ No null in Rust
November 10, 2026
Welcome to episode 13 of Rust for Beginners, a series for complete beginners (no programming experience needed). In the last video, we learned about enums and match. Today, we'll meet the most used enum of all: Option. It's how Rust handles a missing value, without null. Every code block below is the exact code from the video, ready to copy.
- All episodes are in the Rust Tutorial for Beginners playlist.
- Versions used: Rust 1.99.1 on Ubuntu, VS Code with rust-analyzer.
1. New project
Let's create a new project called options, and open it in VS Code.
cargo new options
cd options2. Null problem
In many languages, any value can secretly be null, and using it crashes the program. Its inventor called null his billion-dollar mistake.
3. Option
Rust has no null. Instead, a value that might be missing is an Option. It's an enum with two variants: Some, holding a value, or None.
4. Example
Here, some number is Some five, and no number is None. Both have the type Option of i thirty-two.
src/main.rs:
fn main() {
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
let total = some_number + 1;
}Error:
error[E0369]: cannot add `{integer}` to `Option<i32>`
--> src/main.rs:5:29
|
5 | let total = some_number + 1;
| ----------- ^ - {integer}
| |
| Option<i32>
|
note: `Option<i32>` does not implement `Add<{integer}>`
--> /rustc/58cf8f9c35b355199727627d07ae34a4e2846d67/library/core/src/option.rs:598:0
|
= note: `Option<i32>` is defined in another crate5. Error
Can we just add one to it?
cargo run6. Why
No. An Option of i thirty-two is not an i thirty-two. Rust makes us check for None first, so we can never forget.
7. Return Option
Functions return Option when there might be no answer. divide returns None when we try to divide by zero.
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
fn main() {
for b in [4.0, 0.0] {
match divide(10.0, b) {
Some(result) => println!("10 / {b} = {result}"),
None => println!("10 / {b}: can't divide by zero!"),
}
}
}Output: 10 / 4 = 2.5 / 10 / 0: can't divide by zero!
8. match
To use the result, we match on it. In the Some arm, we get the value out. In the None arm, we handle the missing case.
9. Run B
Let's run it. Ten divided by four is two point five, and dividing by zero is handled, without a crash.
cargo run10. get
Many standard functions return Option. get on an array gives Some item if the index exists, or None if it doesn't.
fn main() {
let names = ["Ann", "Bob", "Cat"];
if let Some(name) = names.get(1) {
println!("Found: {name}");
}
let missing = names.get(10);
println!("Index 10: {missing:?}");
println!("Or default: {}", missing.unwrap_or(&"nobody"));
println!("{}", missing.unwrap());
}Output (verified in the VM; the thread number changes every run):
Found: Bob
Index 10: None
Or default: nobody
thread 'main' (3496) panicked at src/main.rs:12:28:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace11. if let
When we only care about the Some case, if let is shorter than match.
12. unwrap_or
unwrap or gives us the value, or a default if it's None.
13. unwrap
And unwrap takes the value out, but if it's None, the program panics. Let's see.
14. Run C
Found Bob, index ten is None, the default is nobody, and then the unwrap panics. So use unwrap only when you're sure there's a value.
cargo runRecap
To recap: Rust has no null. A maybe-missing value is an Option: Some or None. Use match or if let to get the value, unwrap or for a default, and unwrap only when you're sure.