C.W.K.
Stream
Lesson 02 of 06 · published

Option — The Null That Isn't

~12 min · enums, option, null, safety

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Tony Hoare called null references his 'billion-dollar mistake' — decades of crashes, security holes, and 3 a.m. pages, all from a value that claims to be there and isn't. Rust simply doesn't have null. In its place: Option.

Absence as a type

Option<T> is an enum with two variants: Some(T) (a value is present) and None (nothing here). 'Might be missing' becomes part of the type. A function that may not find a result returns Option<T>, and the caller cannot use the inner value without first dealing with the None case. The compiler won't let absence slip through.

You must open the box

You can't accidentally use an Option as if it were the value inside — they're different types. To get at the T, you match on it, use if let, or call a method like unwrap_or. The 'forgot to null-check' bug is gone: the type system makes the check mandatory.

No null means no null-pointer exceptions — ever, in safe Rust. The entire category of 'it was supposed to be there' crashes is gone, not reduced. You handle absence exactly once, where it happens, and the compiler proves you didn't forget.

unwrap: the loaded gun

.unwrap() extracts the value but panics if it's None. It's fine in examples and tests; in real code it's a deliberate 'I'm certain this is Some, crash if I'm wrong.' Prefer unwrap_or, unwrap_or_else, map, or a match — they make you say what happens when the value is absent, instead of crashing.

Code

Option makes 'maybe missing' explicit·rust
fn find_even(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            return Some(n); // found one
        }
    }
    None // nothing matched — and the type SAYS so
}

fn main() {
    let data = [1, 3, 5, 8, 9];
    match find_even(&data) {
        Some(n) => println!("first even: {n}"),
        None => println!("no evens"),
    }
    // Safe default instead of a panic:
    let first = find_even(&[1, 3]).unwrap_or(-1);
    println!("{first}");
}

External links

Exercise

Write fn first_char(s: &str) -> Option<char> that returns the first character, or None for an empty string. Call it on a normal string and an empty one, handling both with a match. Then rewrite the call using unwrap_or('?'). When would you prefer each style?
Hint
match is right when both cases need real handling; unwrap_or is right when a sensible default covers the None case. Neither one can ignore None — that's the point.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.