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).