Here's a little crate with a solution to a problem I've encountered a number of times. I've had this sitting on my disk for a while, but it's basically done, so publishing it now to see what people think. I haven't actually used this in real code, and there may well be much better solutions for what it's trying to do.
Fairly often I am building a vector over fallible operations, resulting in iterating over a bunch of Result
s. What I really want though is not a collection of Result<T>
but a collection of T
. It hasn't been obvious to me how to deal with those situations ergonomically.
So this crate provides a one-liner that either: extracts every element of the iterator over Result<T>
into an iterator over T
; or, returns an error.
The fail_fast_if_err
method stops at the first error, while the fail_slow_if_err
iterates over all elements and provides access to every error in the collection.
Here's a full example:
extern crate result_iter;
use result_iter::ResultIterExt;
use std::io::{self, Read, BufReader};
use std::fs::{self, File};
fn run() -> Result<Vec<String>, io::Error> {
// Read a directory of files into a Vec, where each
// file my generate an error
let maybe_strings;
maybe_strings = fs::read_dir(".")?
.map(|dirent| {
dirent.and_then(|d| File::open(d.path()))
.and_then(|f| {
let mut f = BufReader::new(f);
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)})
});
// As soon as we encounter an error, return it.
// Otherwise return a Vec<String>
let strings = maybe_strings.fail_fast_if_err()?.collect();
Ok(strings)
}
fn main() {
let _ = run();
}
Note that these methods, though they accept iterators and return iterators, need to drain the original iterator, scanning for errors, and so use temporary storage proportional to the number of elements.
What do you think? What other ways are there to solve this problem?