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