Description
Proposal
Problem statement
I regularly run into the situation where I have two Option
s, and I'd like to combine them in some way. This almost always takes the the following form:
match (l, r) {
(Some(l), Some(r)) => Some(f(l, r)),
(Some(l), None) => Some(l),
(None, Some(r)) => Some(r),
(None, None) => None,
}
This is rather verbose for ultimately something very simple.
Motivating examples or use cases
Searching for (Some(l), Some(r)) =>
on Github already gives dozens and dozens of matches for this pattern, even when looking for specific variable names (l
and r
). Not all results (or even most) of this query match this pattern, but there is still a lot of matches for it.
Solution sketch
I think we should add the following function to Option<T>
:
impl<T> Option<T> {
pub fn reduce<F: FnOnce(T, T) -> T>(self, other: Option<T>, f: F) -> Option<T> {
match (self, other) {
(Some(l), Some(r)) => Some(f(l, r)),
(Some(l), None) => Some(l),
(None, Some(r)) => Some(r),
(None, None) => None,
}
}
}
I'm open to other names than reduce
, it just seemed follow Iterator::reduce
closely.
Alternatives
There is the following alternative,
l.into_iter().chain(r).reduce(f)
but this is quite a bit longer and less readable/clean in my opinion (and also requires a FnMut
). There is also the micro-crate opt_reduce
which adds precisely this method in an extension trait.
Unlike a lot of other methods on Option
s, it is not possible to neatly express this method using ?
short-circuiting. The status quo essentially always boils down to the above verbose match statement. If you know your pattern jutsu well you can avoid one extra match arm (but I'm not sure if it's actually more readable):
match (l, r) {
(Some(l), Some(r)) => Some(f(l, r)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
}
Links and related work
This has been discussed and closed before:
Nevertheless, I still run into this pattern a lot and I think Option::reduce
is quite a natural addition to the standard library, so I'm bringing it up again.