The accepted answer shows how to stop on error while collecting, as requested in the question. However it won't help with large or infinite fallible iterators, or when the iterator is collected by code you don't control. If your use case is one of those, read on.
As already noted, for can be used to emulate stop-on-error, but that is sometimes inelegant, as when you want to call max() or other method that consumes the iterator. In other situations it's next to impossible, as when the iterator is consumed by code in another crate, such as itertools or Rayon1.
Iterator consumer: try_for_each
When you control how the iterator is consumed, you can just use try_for_each to stop on first error. It accepts a closure that returns a Result, and try_for_each() will return Ok(()) if the closure returned Ok every time, and the first Err on the first error. This allows the closure to detect errors simply by using the ? operator in the natural way:
use std::{fs, io}; fn main() -> io::Result<()> { fs::read_dir("/")?.try_for_each(|e| -> io::Result<()> { println!("{}", e?.path().display()); Ok(()) })?; // ... Ok(()) }
If you need to maintain state between the invocations of the closure, you can also use try_fold. Both methods are implemented by ParallelIterator, so the same pattern works with Rayon.
try_for_each() does require that you control how the iterator is consumed. If that is done by code not under your control - for example, if you are passing the iterator to itertools::merge() or similar, you will need an adapter.
Iterator adapter: scan
The first attempt at stopping on error is to use take_while:
use std::{io, fs}; fn main() -> io::Result<()> { fs::read_dir("/")? .take_while(Result::is_ok) .map(Result::unwrap) .for_each(|e| println!("{}", e.path().display())); // ... Ok(()) }
This works, but we don't get any indication that an error occurred, the iteration just silently stops. Also it requires the unsightly map(Result::unwrap) which makes it seem like the program will panic on error, which is in fact not the case as we stop on error.
Both issues can be addressed by switching from take_while to scan, a more powerful combinator that not only supports stopping the iteration, but passes its callback owned items produced by the iterator, allowing the closure to extract the error:
fn main() -> io::Result<()> { let mut err = Ok(()); fs::read_dir("/")? .scan((), |_, item| item.map_err(|e| err = Err(e)).ok()) .for_each(|e| println!("{}", e.path().display())); err?; // ... Ok(()) }
Playground
The closure passed to scan() uses map_err() to detect the error and extract it, and Result::ok() to convert the useless Result::<T, ()> returned by map_err() to Some(T) in case of Ok, which continues the iteration, and None in case of Err, which terminates it.
These examples trivially exhaust the iterator with for_each(), but one can chain it with arbitrary manipulations, including Rayon's par_bridge(). Using scan() it is even possible to collect() the items into a container and have access to the items seen before the error, which is sometimes useful and not possible when collecting into Result<Container, Error>.
1 Needing to use par_bridge() comes up when using Rayon to process streaming data in parallel:
fn process(input: impl BufRead + Send) -> std::Result<Output, Error> { let mut err = Ok(()); let output = lines .input() .scan((), |_, item| item.map_err(|e| err = Err(e)).ok()) .par_bridge() .map(|line| ... executed in parallel ... ) .reduce(|item| ... also executed in parallel ...); err?; ... Ok(output) }
Again, equivalent effect cannot be trivially achieved by collecting into Result.