Converting a Result<Vec, Error> to Result<Vec, CustomError>

Hello,

I have a Vec<String> that I want to parse into a Vec<serde_json::Value>. Using map/collect I then get a Result<Vec<serde_json::Value>, serde_json::Error>. Now I also want to convert that to a Result<Vec<serde_json::Value>, MyCustomError>.

So far I have this code. It doesn’t compile:

error[E0277]: a collection of type `std::result::Result<std::vec::Vec<_>, MyError>` cannot be built from an iterator over elements of type `std::result::Result<_, serde_json::Error>`
  --> src/main.rs:26:82
   |
26 |     let res: Result<Vec<_>, MyError> = v.iter().map(|e| serde_json::from_str(e)).collect();
   |                                                                                  ^^^^^^^ a collection of type `std::result::Result<std::vec::Vec<_>, MyError>` cannot be built from `std::iter::Iterator<Item=std::result::Result<_, serde_json::Error>>`
   |
   = help: the trait `std::iter::FromIterator<std::result::Result<_, serde_json::Error>>` is not implemented for `std::result::Result<std::vec::Vec<_>, MyError>`

I don’t really understand this error. Right now I have to resort to the commented code to get the correct return value.

map_err(|e| MyError::from(e)) does the same as the match. Can apply after collect or serde_json::from_str(e)

1 Like

Thanks jonh. So when I apply map_err to serde_json::from_str() it works without anything else, which is good.

However when I apply it to the result of collect type inference fails. I can make it work with this code:

let res = v
    .iter()
    .map(|e| serde_json::from_str(e))
    .collect::<Result<Vec<_>, serde_json::Error>>()
    .map_err(|e| MyError::from(e));

Without the type annotation it fails with this error:

error[E0282]: type annotations needed
  --> src/main.rs:26:40
   |
26 |       let res: Result<Vec<_>, MyError> = v
   |  ________________________________________^
27 | |         .iter()
28 | |         .map(|e| serde_json::from_str(e))
29 | |         .collect()
   | |__________________^ cannot infer type for `B`
   |
   = note: type must be known at this point

I’m just curious why the compiler can’t infer the type here.

The reason is that the collect() function is very general. The Rust standard library documentation says:

Because collect() is so general, it can cause problems with type inference. As such, collect() is one of the few times you’ll see the syntax affectionately known as the ‘turbofish’: ::<> . This helps the inference algorithm understand specifically which collection you’re trying to collect into.

collect() could be used to turn this iterator into many different types, not just Vec. Any type that implements FromIter could be created using collect().

In this case, you were able to tell Rust what type of collection you wanted, but you didn’t have to specify the element type for Vec.

I think you could even just tell Rust that you want a Result using:

.collect::<Result<_, _>>()

The compiler will infer the types for the two _.

The code compiles, but you would have to check that it does what you expect.

1 Like