I am trying to generify a Result
that is returned by the reqwest::blocking::get
function. It returns a Result<reqwest::blocking::Response, reqwest::Error>
but the function it is called in returns a Result<reqwest::blocking::Response, Box<dyn std::error::Error>
.
- Why does my first attempt fail to compile?
- What is the most idiomatic way to make this conversion?
This is the first attempt:
fn get_example_fails() -> Result<Response, Box<dyn Error>> {
let result = blocking::get("http://example.com");
result.map_err(|error| Box::new(error))
}
It has the following error which I do not know how to fix but feel it might be more idiomatic with some minor tweaking - but am not sure what to tweak:
error[E0308]: mismatched types
--> src/bittrex.rs:143:9
|
141 | fn get_example_fails() -> Result<Response, Box<dyn Error>> {
| -------------------------------- expected `Result<reqwes
t::blocking::Response, Box<(dyn StdError + 'static)>>` because of return type
142 | let result = blocking::get("http://example.com");
143 | result.map_err(|error| Box::new(error))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn StdError`,
found struct `reqwest::Error`
|
= note: expected enum `Result<_, Box<(dyn StdError + 'static)>>`
found enum `Result<_, Box<reqwest::Error>>`
This attempt compiles but seems verbose:
fn get_example_works() -> Result<Response, Box<dyn Error>> {
let result = blocking::get("http://example.com");
match result {
Ok(resp) => Ok(resp),
Err(error) => Err(Box::new(error)),
}
}
This is because the compiler is overzealous here. Notice that
Box::new(error)
simply putserror
in aBox
, so you get a strongly typedBox<reqwest:Error>
, just as the compiler reports. But what you want is a trait object fordyn Error
, whichBox<reqwest:Error>
could be, but the compiler does not make that type-weakening assumption. The fix is manually make that clear:Notice the extra
as Box<dyn Error>
, which makes clear to the compiler that the value should be downcasted and where the fact that it is a concretereqwest::Error
that is in theBox
should be erased.The reason why the second example compiles fine is type inference. In the first example, the type returned from the function will be the type returned from
.map_err()
, which is aResult<Response, Box<reqwest::Error>>
- that's it. The type does not match, so you get an error. In the second example, thematch
constructs a whole newResult
and Rust has to infer theT
andE
for that; so type inference kicks in. In this second case, the inference due to the function's return value is successful with respect toBox<dyn Error>
. But withmap_err()
, the type inference can't "look into" themap_err()
-body and weaken it's return type; its already too late, the type is fixed. That's why you have to do it manually as advised above.