I'm looking for a way to eliminate the temporary vector allocation in this example:
fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
println!(
"{}: {:?}",
name,
iter.collect::<Result<Vec<_>, _>>()
.map(|v| v.into_iter().min())
);
}
fn main() {
let without_errors = vec![Ok(1), Ok(2), Ok(3)];
let with_errors = vec![Ok(1), Err("error"), Ok(2)];
doit("without errors", without_errors.into_iter());
doit("with errors", with_errors.into_iter());
}
This is a variation of the iterator with error handling theme, except that I don't want to create a collection (so collect()
doesn't quite do the job), but I want to perform further operations on the elements being iterated over.
Note that this gives the wrong result because Ok
is less than Err
:
fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
println!("{}: {:?}", name, iter.min());
}
It would give the right result for max()
by accident, but it would not stop iterating on the first error.
"Lifting" a function to handle an iterator of results is a fairly common pattern and, as usual, itertools has a solution —
process_results
:This code began life as
ResultShunt
in the standard library before being extracted to itertools. It's what underlies the implementation ofsum
andproduct
for iterators ofResult
.It's possible to abuse
collect()
for this:This can be used via
iter.collect::<Min<_>>().value()
. This is a lot of machinery, and I don't see a way to abstract over it (so that you only need to supplystd::cmp::min
or some other semigroup operation).I didn't look in the direction of
Iterator::try_fold
, which provides most of the machinery.Iterator::try_fold
provides the framework for what you need, and it's available since Rust 1.27 (Playground):Before that, I think your only option is manually iterating (Playground)