- Feature Name: option_filter_map
- Start Date: 2018-11-30
- RFC PR:
- Rust Issue:
Summary
This RFC proposes the addition of Option::filter_map to combine the functionality of Option::filter and Option::map.
Motivation
Given the following optional collection of optional items, we would like to write a function that gives us the n-th element, or None:
fn main() {
let things: Option<Vec<Option<u8>>> = Some(vec![Some(1), None, Some(3)]);
assert_eq!(get_elem(&things, 0), Some(1));
assert_eq!(get_elem(&things, 1), None);
assert_eq!(get_elem(&things, 2), Some(3));
}
fn get_elem(coll: &Option<Vec<Option<u8>>>, n: usize) -> Option<u8> {
// ...
}
Using only the existing Option::map function, we can cover the case where the n-th element is a Some(_) perfectly well, but for handling the None case we can either panic, use a magic value or return Some(None), none of which is satisfactory:
// assume that vec[n] == None
coll.as_ref().map(|vec| vec[n].unwrap()) // this panics
coll.as_ref().map(|vec| vec[n].unwrap_or(42)) // this returns 42
coll.as_ref().map(|vec| vec[n]) // this returns Some(None)
We can solve this by checking for the vec[n] == None situation beforehand using Option::filter:
fn get_elem(coll: &Option<Vec<Option<u8>>>, n: usize) -> Option<u8> {
coll.as_ref()
.filter(|vec| vec[n].is_some())
.map(|vec| vec[n].unwrap())
}
This works, but is needlessly convoluted and requires looking up the n-th element twice. This could be combined into a single Option::filter_map call, which could be used like this:
fn get_elem(coll: &Option<Vec<Option<u8>>>, n: usize) -> Option<u8> {
coll.as_ref().filter_map(|vec| vec[n])
}
And implemented like this:
pub trait OptionFilterMap<T> {
fn filter_map<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U>;
}
impl<T> OptionFilterMap<T> for Option<T> {
fn filter_map<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
match self {
Some(x) => f(x),
None => None
}
}
}
Rationale and alternatives
This would simplify a common use case. An alternate but more generic solution is already possible:
coll.as_ref().map_or(None, |vec| vec[n])
The proposed new function would be equivalent to using Option::map_or with None as the default value.
The rationale for including this as a separate function anyway is discoverability and functional similarity to Iterator::filter_map and its special case Iterator::find_map.