SyntaxStudy
Sign Up
Rust Beginner 1 min read

Option<T> and if let

`Option` is Rust's solution to null values. It is an enum defined in the standard library with two variants: `Some(T)`, which wraps a value, and `None`, which represents the absence of a value. The compiler forces you to handle both cases before you can use the inner value, eliminating null pointer exceptions at compile time. Unlike `null` in other languages, a bare `Option` can never be accidentally dereferenced. The `if let` syntax is a concise alternative to `match` when you only care about one variant. `if let Some(x) = option_value { ... }` executes the block only if the option is `Some`, binding the inner value to `x`. There is also `while let`, which loops as long as a pattern matches. Both constructs make code that processes optional values much more readable than nested `match` expressions. The `Option` type comes with a rich API of combinators. `.unwrap()` panics if the value is `None`; `.expect("message")` panics with a custom message. Safer alternatives include `.unwrap_or(default)`, `.unwrap_or_else(|| compute())`, `.map(|x| transform(x))`, `.and_then(|x| option_returning_fn(x))`, and `.filter(|x| predicate(x))`. Learning to chain these combinators is a key skill for writing idiomatic Rust.
Example
fn find_first_even(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            return Some(n);
        }
    }
    None
}

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 { None } else { Some(a / b) }
}

fn main() {
    let numbers = vec![1, 3, 7, 4, 9];

    // if let — only runs when Some
    if let Some(even) = find_first_even(&numbers) {
        println!("first even: {even}");
    } else {
        println!("no even numbers found");
    }

    // Combinators
    let result = find_first_even(&numbers)
        .map(|n| n * n)
        .unwrap_or(0);
    println!("first even squared (or 0): {result}");

    // Chaining Options
    let doubled = divide(10.0, 2.0)
        .and_then(|q| divide(q, 2.0))
        .map(|v| v * 3.0);
    println!("chained result: {:?}", doubled);

    // Division by zero
    println!("10 / 0 = {:?}", divide(10.0, 0.0));

    // while let — pop from a stack until empty
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        print!("{top} ");
    }
    println!();

    // unwrap_or_else
    let odds_only = vec![1, 3, 5];
    let val = find_first_even(&odds_only).unwrap_or_else(|| -1);
    println!("fallback value: {val}");
}