Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the most idiomatic way of working with an Iterator of Results? [duplicate]

I have code like this:

let things = vec![/* ...*/]; // e.g. Vec<String> 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::<Result<Vec<_>, _>>() 

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::<Result<Vec<_>, _>>, and it works great. I'm specifically trying to eliminate the following boilerplate:

  • In the filter's closure, I have to use match on thing_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 statement let a = try!(thing_result); in order to deal with the possibility of an Err. 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?

like image 813
Tim McLean Avatar asked Apr 02 '16 03:04

Tim McLean


2 Answers

There are lots of ways you could mean this.

If you just want to panic, use .map(|x| x.unwrap()).

If you want all results or a single error, collect into a Result<X<T>>:

let results: Result<Vec<i32>, _> = result_i32_iter.collect(); 

If you want everything except the errors, use .filter_map(|x| x.ok()) or .flat_map(|x| x).

If you want everything up to the first error, use .scan((), |_, x| x.ok()).

let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok()); 

Note that these operations can be combined with earlier operations in many cases.

like image 80
Veedrac Avatar answered Sep 19 '22 05:09

Veedrac


Since Rust 1.27, Iterator::try_for_each could be of interest:

An iterator method that applies a fallible function to each item in the iterator, stopping at the first error and returning that error.

This can also be thought of as the fallible form of for_each() or as the stateless version of try_fold().

like image 40
arkod Avatar answered Sep 23 '22 05:09

arkod