patternrustMinor
What's the most idiomatic way of working with an Iterator of Results?
Viewed 0 times
idiomaticwithiteratortheworkingwaywhatmostresults
Problem
I have code like this:
In terms of semantics, I want to stop processing after the first error.
The above code works, but it feels quite cumbersome. Is there a better way? I've looked through the docs for something like
I am aware of
Is there another approach I can use to get this level of conciseness, or do I just need to tough it out?
let things = vec![/* ...*/]; // e.g. Vec
things
.map(|thing| {
let a = try!(do_stuff(thing));
Ok(other_stuff(a))
})
.filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
})
.map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
})
.collect::, _>>()In terms of semantics, I want to stop processing after the first error.
The above code works, but it feels quite cumbersome. Is there a better way? I've looked through the docs for something like
filter_if_ok, but I haven't found anything.I am aware of
collect::, _>>, and it works great. I'm specifically trying to eliminate the following boilerplate:- In the filter's closure, I have to use
matchonthing_result. I feel like this should just be a one-liner, e.g..filter_if_ok(|thing| check(a)).
- Every time I use
map, I have to include an extra statementlet a = try!(thing_result);in order to deal with the possibility of anErr. Again, I feel like this could be abstracted away into.map_if_ok(|thing| ...).
Is there another approach I can use to get this level of conciseness, or do I just need to tough it out?
Solution
You can implement these iterators yourself. See how
Your code may look like this:
playground
filter and map are implemented in the standard library.map_ok implementation:#[derive(Clone)]
pub struct MapOkIterator {
iter: I,
f: F,
}
impl Iterator for MapOkIterator
where
F: FnMut(A) -> B,
I: Iterator>,
{
type Item = Result;
#[inline]
fn next(&mut self) -> Option {
self.iter.next().map(|x| x.map(&mut self.f))
}
}
pub trait MapOkTrait {
fn map_ok(self, func: F) -> MapOkIterator
where
Self: Sized + Iterator>,
F: FnMut(A) -> B,
{
MapOkIterator {
iter: self,
f: func,
}
}
}
impl MapOkTrait for I
where
I: Sized + Iterator>,
{
}filter_ok is almost the same:#[derive(Clone)]
pub struct FilterOkIterator {
iter: I,
predicate: P,
}
impl Iterator for FilterOkIterator
where
P: FnMut(&A) -> bool,
I: Iterator>,
{
type Item = Result;
#[inline]
fn next(&mut self) -> Option> {
for x in self.iter.by_ref() {
match x {
Ok(xx) => if (self.predicate)(&xx) {
return Some(Ok(xx));
},
Err(_) => return Some(x),
}
}
None
}
}
pub trait FilterOkTrait {
fn filter_ok(self, predicate: P) -> FilterOkIterator
where
Self: Sized + Iterator>,
P: FnMut(&A) -> bool,
{
FilterOkIterator {
iter: self,
predicate: predicate,
}
}
}
impl FilterOkTrait for I
where
I: Sized + Iterator>,
{
}Your code may look like this:
["1", "2", "3", "4"]
.iter()
.map(|x| x.parse::().map(|a| a + 10))
.filter_ok(|x| x % 2 == 0)
.map_ok(|x| x + 100)
.collect::, std::num::ParseIntError>>()playground
Code Snippets
#[derive(Clone)]
pub struct MapOkIterator<I, F> {
iter: I,
f: F,
}
impl<A, B, E, I, F> Iterator for MapOkIterator<I, F>
where
F: FnMut(A) -> B,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<B, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|x| x.map(&mut self.f))
}
}
pub trait MapOkTrait {
fn map_ok<F, A, B, E>(self, func: F) -> MapOkIterator<Self, F>
where
Self: Sized + Iterator<Item = Result<A, E>>,
F: FnMut(A) -> B,
{
MapOkIterator {
iter: self,
f: func,
}
}
}
impl<I, T, E> MapOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}#[derive(Clone)]
pub struct FilterOkIterator<I, P> {
iter: I,
predicate: P,
}
impl<I, P, A, E> Iterator for FilterOkIterator<I, P>
where
P: FnMut(&A) -> bool,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<A, E>;
#[inline]
fn next(&mut self) -> Option<Result<A, E>> {
for x in self.iter.by_ref() {
match x {
Ok(xx) => if (self.predicate)(&xx) {
return Some(Ok(xx));
},
Err(_) => return Some(x),
}
}
None
}
}
pub trait FilterOkTrait {
fn filter_ok<P, A, E>(self, predicate: P) -> FilterOkIterator<Self, P>
where
Self: Sized + Iterator<Item = Result<A, E>>,
P: FnMut(&A) -> bool,
{
FilterOkIterator {
iter: self,
predicate: predicate,
}
}
}
impl<I, T, E> FilterOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}["1", "2", "3", "4"]
.iter()
.map(|x| x.parse::<u16>().map(|a| a + 10))
.filter_ok(|x| x % 2 == 0)
.map_ok(|x| x + 100)
.collect::<Result<Vec<_>, std::num::ParseIntError>>()Context
Stack Overflow Q#36368843, score: 16
Revisions (0)
No revisions yet.