48

Is there any opinionated and more elegant way to convert Vec<Result<T, E>> to Result<Vec<T>, E>? I want to get Ok<Vec<T>> if all values of vector are Ok<T> and Err<E> if at least one is Err<E>.

Example:

fn vec_of_result_to_result_of_vec<T, E>(v: Vec<Result<T, E>>) -> Result<Vec<T>, E>
where
    T: std::fmt::Debug,
    E: std::fmt::Debug,
{
    let mut new: Vec<T> = Vec::new();

    for el in v.into_iter() {
        if el.is_ok() {
            new.push(el.unwrap());
        } else {
            return Err(el.unwrap_err());
        }
    }

    Ok(new)
}

I'm looking for a more declarative way to write this. This function forces me to write a where clause which will never be used and Err(el.unwrap_err()) looks useless. In other words, the code does many things just to make the compiler happy. I feel like this is such a common case that there's a better way to do it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Arek C.
  • 1,271
  • 2
  • 9
  • 17
  • 1
    You can avoid the `unwrap()`s (and the `Debug` bounds) by using a `match` instead of `is_ok()`. But in this case `collect()` is even cleaner. – Tavian Barnes Sep 08 '20 at 17:26

2 Answers2

50

An iterator over Result<T, E> can be collect()-ed directly into a Result<Vec<T>, E>; that is, your entire function can be replaced with:

let new: Result<Vec<T>, E> = v.into_iter().collect()
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
John Ledbetter
  • 13,557
  • 1
  • 61
  • 80
  • 1
    could you explain this behavior? I dont know why collecting an Iterator would remove the Result structure type from its content – Tobi Sep 08 '21 at 07:57
  • 3
    @Tobi the `collect` method on an iterator (https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect) works for any type that implements the trait `FromIterator`. As stated by @chpio, there is an implementation of `FromIterator` for iterators of `Result`. – alightgoesout Nov 11 '21 at 15:06
  • The `collect` method here is rather flexible. `v.into_iter().collect::>()` return a vector of results. `v.into_iter().collect::,_>>()` returns a result of vector. And presumably if the compiler can figure out which one you want, you don't need the turbo fish. – Troy Daniels Mar 02 '22 at 23:50
  • What happens to the right-hand side of the Result though? Presumably the first Error that is encountered is returned (kind of like a short-circuit)? This seems similar to traversal in functional libraries like cats in Scala, but I believe the Errors would be collected together by `folding` them into one another in cats. – Oli Jul 27 '23 at 22:59
7

You can use the FromIterator trait implementation on Result (.collect() requires FromIterator on the destination type and calls into it for the conversion from Iterator):

fn vec_of_result_to_result_of_vec<T, E>(v: Vec<Result<T, E>>) -> Result<Vec<T>, E> {
    v.into_iter().collect()
}
chpio
  • 896
  • 6
  • 16