BudiBadu Logo
Samplebadu

Rust by Example: Option Handling

Rust 1.75+

Say goodbye to null pointer exceptions. This example demonstrates how to use the Option type to represent the presence or absence of a value, and how to safely handle these cases using combinators like map, unwrap_or, and if let.

Code

fn main() {
    let some_number = Some(5);
    let no_number: Option<i32> = None;
    
    // 1. Using unwrap (Dangerous!)
    // Panics if the value is None
    let x = some_number.unwrap(); 
    println!("Unwrapped: {}", x);
    
    // 2. Using expect (Better panic message)
    // let y = no_number.expect("We expected a number here!");
    
    // 3. Using unwrap_or (Safe default)
    let z = no_number.unwrap_or(0);
    println!("Defaulted: {}", z);
    
    // 4. Using map (Transform inner value)
    let doubled = some_number.map(|n| n * 2);
    println!("Doubled: {:?}", doubled); // Some(10)
    
    // 5. Using and_then (Chain operations)
    let result = some_number
        .and_then(|n| checked_division(n, 2.5)) // Returns Option
        .and_then(|n| checked_division(n, 0.0)); // Returns None
        
    println!("Chained result: {:?}", result);
    
    // 6. if let (Concise matching)
    if let Some(n) = some_number {
        println!("Found number: {}", n);
    }
}

fn checked_division(num: i32, div: f64) -> Option<f64> {
    if div == 0.0 {
        None
    } else {
        Some(num as f64 / div)
    }
}

Explanation

The Option<T> type is Rust's solution to the "billion-dollar mistake" of null references. It forces you to handle the possibility of absence explicitly. Under the hood, Rust uses Niche Optimization to make Option efficient. For example, Option<&T> takes up the same amount of memory as a raw pointer, with None represented by the null address (0), which is invalid for a reference.

To work with Options ergonomically, Rust provides "combinators":

  • map(): Transforms the value inside Some. If the Option is None, it returns None immediately. This is lazy; the closure only runs if a value exists.
  • and_then(): Also known as flatmap. It is used when the transformation itself returns an Option. It prevents nested types like Option<Option<T>>.
  • unwrap_or_else(): Similar to unwrap_or, but takes a closure. This allows for lazy evaluation of the default value, which is crucial if computing the default is expensive (e.g., a database call).

Code Breakdown

13
unwrap_or(0). This is the safe way to extract a value. If no_number is Some(v), it returns v. If it is None, it returns 0. No panic occurs.
17
map(|n| n * 2). This applies the closure n * 2 to the value inside the Option if it exists. If some_number was None, the closure would never run, and the result would be None.
28
if let Some(n) = .... This is syntactic sugar for a match statement that only cares about one variant. It reads: "If some_number matches the pattern Some(n), execute this block."