168. collect::<Result<Vec<_>, _>>() — Bail an Iterator on the First Error

You have a Vec<&str> of numbers to parse and you want to bail the moment one of them is malformed. The hand-rolled loop with ? works, but collect() does the same thing in one line — and most people never realize the trick is in the turbofish.

The pain point is everywhere: parse a row of CSV cells, deserialize a batch of records, convert a list of paths — any time you map a fallible function over an iterator and want the first failure to stop the show.

The naive version builds the Vec by hand and threads ? through a loop:

1
2
3
4
5
6
7
fn parse_all_loop(inputs: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
    let mut nums = Vec::with_capacity(inputs.len());
    for s in inputs {
        nums.push(s.parse::<i32>()?);
    }
    Ok(nums)
}

The same thing in one line, by asking collect to produce a Result<Vec<_>, _> instead of a Vec<Result<_, _>>:

1
2
3
fn parse_all(inputs: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
    inputs.iter().map(|s| s.parse::<i32>()).collect()
}

That works because Result<C, E> (where C: FromIterator<T>) implements FromIterator<Result<T, E>>: feed it an iterator of Results and it yields a single Result. The first Err short-circuits the rest of the iteration; if every item is Ok, you get Ok(collection) back.

1
2
3
4
5
6
7
8
let good = ["1", "2", "3"];
let bad  = ["1", "oops", "3"];

let ok: Result<Vec<i32>, _> = good.iter().map(|s| s.parse()).collect();
assert_eq!(ok.unwrap(), vec![1, 2, 3]);

let err: Result<Vec<i32>, _> = bad.iter().map(|s| s.parse()).collect();
assert!(err.is_err());

The same impl exists for Option, so collect::<Option<Vec<_>>>() bails on the first None:

1
2
3
4
5
let some_all: Option<Vec<u32>> = ["1", "2", "3"].iter().map(|s| s.parse().ok()).collect();
assert_eq!(some_all, Some(vec![1, 2, 3]));

let some_none: Option<Vec<u32>> = ["1", "no", "3"].iter().map(|s| s.parse().ok()).collect();
assert_eq!(some_none, None);

You’re not stuck with Vec either — any FromIterator target works, so Result<HashSet<_>, _> or Result<String, _> from a Chars iterator is just as valid.

Use it whenever you’d otherwise write a fold-the-error loop. The intent reads off the type signature, and the iterator stops cold at the first bad item instead of finishing the parse and then sifting through a Vec<Result<_, _>> afterwards.

← Previous 167. Saturating<T> — Stop Calling .saturating_add() Everywhere