I often find myself wanting this method, am I alone?
impl<T> Option<T> {
fn and_also<U>(self, other: Option<U>) -> Option<(T, U)> {
match (self, other) {
(Some(this), Some(that)) => Some((this, that)),
_ => None,
}
}
}
The shortest way to write this is this:
option.and_then(|this| other.map(|that| (this, that))
Often, this is the lead in to some sort of and_then
or if let
processing that requires both values, making the verbosity of the above rather unpleasant. Its also not easy to read if you don’t know it offhand, in my opinion, requiring reasoning about two higher order functions and closures within closures.
Is this pattern common or is it just my code? Would encapsulating this in a method be useful and worthwhile?
Other reasonable names for this method that occur to me are as_well_as
and merge
. And it also makes sense to include on Result
.
1 Like
dan_t
July 27, 2016, 8:18am
2
Other reasonable names for this method that occur to me are as_well_as and
merge. And it also makes sense to include on Result.
These kind of operations are often called 'zip'. Rust already uses this
name for the same operation on iterators.
1 Like
Actually, you can even write this operation using the iterator version of zip:
let result = option.iter().zip(other).next();
Not sure that’s actually a good idea, though.
You probably want into_iter()
to take it by value.
Then with itertools this becomes just izip!(option, other).next()
.
When you want it for if let
, you could instead pattern match both directly:
if let (Some(this), Some(that)) = (option, other) { /*...*/ }
That's true! And it occurs to me that another way to do this is to impl<T, U> Into<Option<(T, U)> for (Option<T>, Option<U>)
.
Another would be catch { (option?, other?) }
2 Likes
I’d like to see this implemented as a method on tuple cons cells. Hypothetical syntax follows:
trait TupleOfOptions<T: Tuple>: Tuple {
fn all(self) -> Option<T>;
fn any(self) -> Option<T::Dual>;
}
impl TupleOfOptions<()> for () {
fn all(self) -> Option<()> {
Some(())
}
fn any(self) -> Option<!> {
None
}
}
impl<H, T, U> TupleOfOptions<(H, ...T)> for (Option<H>, ...U)
where T: Tuple,
U: TupleOfOptions<T>
{
fn all(self) -> Option<(H, ...T)> {
let (h_opt, ...u) = self;
match (h_opt, u.all()) {
(Some(h), Some(t)) => Some((h, ...t)),
_ => None,
}
}
fn any(self) -> Option<(H | ...T::Dual)> {
let (h_opt, ...u) = self;
match (h_opt, u.any()) {
(Some(h), _) => Some((h | ...!)),
(None, Some(t)) => Some((! | ...t)),
_ => None,
}
}
}
Then you could use it on a tuple of any number of Option
s
if let Some((a, b, c)) = (a_opt, b_opt, c_opt).all() {
...
}
1 Like
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.