Since ProcessResults
implements Iterator
, I don't understand the problem here.
The problem is not in the trait bounds, but in the lifetime of the value returned by filter_lines
. process_results
hands you an iterator that refers to data local to process_results
invocation. You can use that iterator, such as by iterating over it or passing it to functions, but you're not allowed to return it out of the callback, nor to return an iterator that refers to it, as filter_lines()
does. This is because the value returned from the callback is returned by process_results
itself, and that value must not refer to data local to process_results
.
For example, this implementation of filter_lines()
, which goes through the iterator and returns a value based on the values found inside, compiles:
fn filter_lines<I: Iterator<Item = String>>(lines: I) -> Option<usize> {
lines.map(|line| line.len()).max()
}
Likewise, leaving your implementation of filter_lines
, but not returning the iterator from the callback, also compiles:
// with filter_lines as originally defined
let line_max = process_results(reader.lines(), |lines| filter_lines(lines).max());
If you want to build a new iterator that stops when an existing iterator encounters an error, you won't be able to use process_results()
. Instead, you will need to use take_while
or scan
with some outside state, as described here. Applied to your code, scan
would be used like this:
fn parse_file() -> Result<(), Box<dyn std::error::Error>> {
let reader = BufReader::new(File::open("file")?);
let mut err = Ok(());
let line_iter = reader.lines().scan(&mut err, until_err);
let new_iter = filter_lines(line_iter)
// some processing, e.g.:
new_iter.for_each(|line| todo!());
err?; // check whether iteration was ended by error Result
unimplemented!()
}
// filter_lines as originally written...
// helper function for `Iterator::scan()`
fn until_err<T, E>(err: &mut &mut Result<(), E>, item: Result<T, E>) -> Option<T> {
match item {
Ok(item) => Some(item),
Err(e) => {
**err = Err(e);
None
}
}
}