3

Please consider the following two functions:

use std::error::Error;

fn foo() -> Result<i32, Box<dyn Error>> {
    let x: Result<Result<i32, Box<dyn Error>>, &str> = Err("error");
    x?
}

fn bar() -> Result<i32, Box<dyn Error>> {
    Err("error")
}

foo() will compile, but bar() won’t.

I guess I understand why bar() doesn’t work: We expect an object implementing the Error trait in a Box but we pass a &str which neither implements Error nor is it in a Box.

I wonder why foo() compiles though. Won’t the ? operator also try to convert the &str into a Box<dyn Error> in this case? Why does it succeed?

Michael Jung
  • 368
  • 1
  • 8
  • 2
    in `foo()` the `?` operator converts the error to the correct type, while in `bar()` you are not using `?`. It succeeds, because there is such `impl`: `impl<'a> From<&str> for Box { ` – Svetlin Zarev Sep 17 '21 at 21:15
  • 1
    `fn baz() -> Result> { Err("error".into()) }` compiles, and you can think `foo()` as being equivalent to that. – user4815162342 Sep 17 '21 at 21:38

2 Answers2

11

This works because there is an

impl From<&'_ str> for Box<dyn Error>

in the standard library. This is what the ? uses to convert from a &str to a Box<dyn Error>. This implementation in turn uses the impl From<String> for Box<dyn Error> in std, which has a "hidden" type that uses the String as its description when implementing Error.

user2722968
  • 13,636
  • 2
  • 46
  • 67
1

You could make bar work by calling from on Error:

fn bar() -> Result<i32, Box<dyn Error>> {
    Err(Error::from("error"))
}
jeberle
  • 718
  • 3
  • 15