14.4 Best Practices for Using Option<T>

  1. Embrace Option<T>: Use it whenever a value might legitimately be absent. This applies to function return values (e.g., search results, parsing), optional struct fields, and any operation that might “fail” in a non-exceptional way.

  2. Prioritize Safe Handling: Prefer pattern matching (match, if let), basic checks (is_some, is_none), the ? operator (within functions returning Option), or safe methods like unwrap_or, unwrap_or_else, map, and_then, filter, ok_or.

  3. Use unwrap() and expect() Judiciously: Reserve these for situations where None indicates a critical logic error or invariant violation, and immediate program termination (panic) is the desired outcome. Prefer expect("informative message") over unwrap() to aid debugging if a panic occurs.

  4. Leverage Combinators and ? for Conciseness: Chain methods like map, filter, and_then, and use the ? operator to write cleaner, more linear code compared to deeply nested match or if let structures.

    // Chaining example: Find the length of the first word, if any.
    let text = " Example text ";
    let length = text.split_whitespace() // Iterator<Item=&str>
                     .next()             // Option<&str>
                     .map(|word| word.len()); // Option<usize>
    
    match length {
        Some(len) => println!("Length of first word: {}", len),
        None => println!("No words found."),
    }
    
    // Using ? inside a function:
    fn process_maybe_data(data: Option<DataSource>) -> Option<ProcessedValue> {
        let source = data?;               // Propagate None if data is None
        let intermediate = source.step1()?; // Propagate None if step1 yields None
        let result = intermediate.step2()?; // Propagate None if step2 yields None
        Some(result)
    }
  5. Use as_ref() or as_mut() for Borrowing: When you need to work with the value inside an Option<T> via a reference (&T or &mut T) without taking ownership, use my_option.as_ref() or my_option.as_mut(). This yields an Option<&T> or Option<&mut T>, respectively, which is often needed for matching or passing to functions that expect references.