What Option does

A type that represents either Some(T) or None — Rust's answer to null without the null-pointer crashes.

As an enum, Option can be exactly one of its declared variants at any time. The compiler stores a discriminant alongside the variant data, and the layout is optimised — for instance, an enum like Option<&T> uses the null-pointer niche so it fits in the same memory as a single pointer. The full canonical path is std::option::Option; bring it into scope with use std::option::Option; or refer to it by its full path.

When to use it

Use Option for any value that may be absent: missing config, lookup misses, optional struct fields, the return value of `parse()`. The compiler will force you to handle the None case, eliminating an entire class of NullPointerException-style bugs.

Annotated examples

Combinator chain instead of nested match

fn parse_port(raw: Option<&str>) -> u16 {
    // map: transform Some(x) -> Some(f(x)), leave None alone
    // and_then: like map but for fallible transforms returning Option
    // unwrap_or: provide a default value if None
    raw.and_then(|s| s.parse::<u16>().ok())
        .filter(|&p| p > 1024)
        .unwrap_or(8080)
}

A chain of map / and_then / filter / unwrap_or is almost always shorter and clearer than the equivalent match expression for Option pipelines.

For deeper background, see the canonical Rust patterns reference for the broader context behind this section.

Pattern matching with if let for the common case

struct Config { backup_path: Option<String> }

fn use_backup(cfg: &Config) {
    // if let is concise when you only care about one variant.
    if let Some(path) = cfg.backup_path.as_deref() {
        println!("Using backup at {path}");
    } else {
        println!("No backup configured");
    }
}

if let is the right tool when you only need to act on the Some case. Use a full match when you need to handle both variants exhaustively.

Common pitfalls

Calling `.unwrap()` on Option in production code is a code smell — it panics if the value is None and gives no context. Prefer `.expect("why this should never be None")`, `.unwrap_or(default)`, `.unwrap_or_else(|| default())`, or the `?` operator inside a function returning `Option<T>`.

For deeper background, see an in-depth Rust idioms cheat sheet for the broader context behind this section.

Performance & threading notes

The size of an enum is the size of its largest variant plus a discriminant tag (often optimised away by niche analysis). Pattern matching compiles to a single jump table; matching is essentially free at runtime.