Option<T> and Result<T, E> in Depth

filter() and flatten()

The .filter() and .flatten() methods on Option<T> refine optional values: .filter(predicate) turns Some(x) into None if x fails a boolean test, and .flatten() collapses a nested Option<Option<T>> into a single Option<T>.

Signatures and Truth Tables

impl<T> Option<T> {
    pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Option<T>;
}

impl<T> Option<Option<T>> {
    pub fn flatten(self) -> Option<T>;
}
Input Value .filter(pred) Result .flatten() Result (on Option<Option<T>>)
None None (pred not called) None
Some(x) where pred(&x) == true Some(x) —
Some(x) where pred(&x) == false None (x dropped) —
Some(None) — None
Some(Some(x)) — Some(x)

Notice that .filter() passes &T by shared reference to the predicate so that if predicate(&x) returns true, ownership of x is preserved inside Some(x).

Validating Optional Inputs with .filter()

.filter() replaces if let Some(x) = opt { if condition(&x) { Some(x) } else { None } } else { None } with a single combinator call:

fn parse_valid_port(raw: Option<&str>) -> Option<u16> {
    raw.map(str::trim)
        .filter(|s| !s.is_empty())
        .and_then(|s| s.parse::<u16>().ok())
        .filter(|&port| port >= 1024) // Keep only unprivileged ports >= 1024
}

fn main() {
    for sample in [Some(" 8080 "), Some("80"), Some("   "), None] {
        println!("{sample:?} -> {:?}", parse_valid_port(sample));
    }
}
Some(" 8080 ") -> Some(8080)
Some("80") -> None
Some("   ") -> None
None -> None

Unwrapping Nested Options with .flatten()

When looking up an optional field inside a struct that was itself returned as an Option (for example, map.get("key").map(|u| u.nickname.as_deref()), which produces Option<Option<&str>>), calling .flatten() removes one layer of Option:

fn main() {
    let nested_some: Option<Option<i32>> = Some(Some(42));
    let inner_none: Option<Option<i32>> = Some(None);
    let outer_none: Option<Option<i32>> = None;

    println!("Some(Some(42)).flatten() = {:?}", nested_some.flatten());
    println!("Some(None).flatten()     = {:?}", inner_none.flatten());
    println!("None.flatten()           = {:?}", outer_none.flatten());
}
Some(Some(42)).flatten() = Some(42)
Some(None).flatten()     = None
None.flatten()           = None

Note that opt.map(f).flatten() is always equivalent to opt.and_then(f).