Chapter 10

Option<T>: When a Value Might Be Missing

👋 Anyone can read and edit this exercise. Sign up to save your progress.

I looked for a joke about null pointers in Rust, but there was None.

Rust has no null. Instead, when a value might be absent, the type makes that explicit using Option<T>:

enum Option<T> {
    Some(T),
    None,
}

The compiler will not let you accidentally use a None as if it were a real value. Every time you have an Option, you have to deal with both cases. That's the whole point.

There are two main ways to unwrap an option. Pattern matching is the fundamental tool:

match find_user(id) {
    Some(name) => println!("found {name}"),
    None => println!("no such user"),
}

But for common cases there are shorter combinators:

let port = settings.port.unwrap_or(8080);     // value or fallback
let upper = name.map(|s| s.to_uppercase());   // transform if Some
let len = maybe_str.map_or(0, |s| s.len());   // transform-or-default

A note on |x| ... (closures)

Those |s| s.to_uppercase() and |s| s.len() bits are closures: anonymous functions you can pass as arguments. The pipes hold the parameters; everything after them is the body:

let add_one = |x| x + 1;
add_one(2); // 3

If the body needs multiple statements, wrap it in braces:

let greet = |name: &str| {
    let trimmed = name.trim();
    format!("hello, {trimmed}")
};

Closures show up properly in chapter 15. For this chapter, just read |s| s.len() as "a tiny one-shot function that takes s and returns s.len()."

A useful one to know: if let lets you handle just the Some case without writing a full match:

if let Some(user) = find_user(id) {
    println!("welcome, {user}");
}

Many standard-library methods return Option. .first(), .last(), .next() on iterators, .get() on slices and maps, .find(...) on iterators. You'll meet Option everywhere.

Option with a fallback

The simplest Option pattern: you're handed one, and you either use what's inside or fall back to a default. match works, and Option also has a few helper methods that are shorter.

Useful from the standard library

  • Option::unwrap_or returns the inner value when Some, the argument when None. The reach-for-this-first method.
  • Option::unwrap_or_default is the same idea but uses T::default() (which for u32 is 0).
  • A match always works too: match setting { Some(v) => v, None => default }. Pick whichever reads better.
Exercise 1 of 4
Open in Web Editor

Results

    Compiler / runtime output
    
                

    Transforming the inside

    Same idea as before, but now the fallback isn't the value itself. You need to call .len() on the inner string first. A match makes both branches explicit; iterator-style methods on Option are tidier once you spot them.

    Useful from the standard library

    • Option::map applies a function inside the Some and leaves None alone. So maybe.map(|s| s.len()) produces an Option<usize>.
    • Option::map_or collapses both steps into one call: a default for None and a closure for Some. Reads as maybe.map_or(0, |s| s.len()).
    • The chapter intro explains the |s| ... closure syntax. For now read it as a tiny one-shot function from s to its body.
    Exercise 2 of 4
    Open in Web Editor

    Results

      Compiler / runtime output
      
                  

      Producing an `Option<char>`

      Now you have to produce an Option, not consume one. Asking a string for its first character is the canonical example: if the string is empty, there is no first character, and returning some "default" char would be a lie. Option<char> is the honest type.

      You could pattern-match by hand, but the standard library has already done the work for you: text.chars() returns an iterator, and every iterator's .next() already hands you Option<Item>. Compose the two.

      Useful from the standard library

      • str::chars returns an iterator over the chars of the string.
      • Iterator::next pulls one item off the iterator and returns it as Option<Item>. For the first character, that's Option<char> and exactly the return type.
      Exercise 3 of 4
      Open in Web Editor

      Results

        Compiler / runtime output
        
                    

        Searching a collection

        The trickiest of the four: produce an Option by searching. The iterator chapter is still ahead, but slice::iter() plus a search combinator already gets you most of the way; the matched tuple still needs to be reduced down to just the username.

        Type walk-through (this is the puzzle):

        name.as_str() turns the &String we destructured out of the tuple into the &str the signature wants.

        Useful from the standard library

        • <[T]>::iter yields shared references to the slice's items, one at a time.
        • Iterator::find takes a predicate closure and returns the first matching item as an Option.
        • Option::map transforms the inner value when present. Here it pulls the username out of the tuple and converts it to &str.
        • String::as_str is the explicit "borrow this String as &str" call.
        Exercise 4 of 4
        Open in Web Editor

        Results

          Compiler / runtime output
          
                      

          Wrapping up `Option`

          You consumed Options with fallbacks and combinators, produced new ones from string and slice operations, and chained find and map to turn a search into the exact return type the signature asked for.

          What we learned

          • Option<T> is Rust's stand-in for "value or absence". The compiler forces both cases to be handled, which is why there's no null.
          • match is the always-works tool. For common patterns, reach for unwrap_or, map, and map_or to keep call sites short.
          • if let Some(x) = ... is the lighter alternative to match when you only care about the Some branch.
          • Option::map mirrors the iterator method of the same name: it transforms the inside if present, leaves None alone.
          • Many standard-library operations already return Option: iter().next(), slice.first(), Vec::pop, HashMap::get, iter().find(...). Once you spot them, "I have an option" is often the answer to "how do I express absence here?".
          • unwrap and expect extract the value but panic on None. Use them in tests or when you've already ruled out None; otherwise prefer the safer combinators.
          • The |x| ... syntax is a closure: a tiny anonymous function. It shows up everywhere with Option and iterators. Chapter 15 covers closures in their own right.
          Next chapter 11Result<T, E>: When an Operation Might Fail