A problem about dyn std::error

I am confused about a problem, the code snippet as following.

The playgroud link is here

    // I define a customed error here
    #[derive(Debug)]
    struct UserErr<'a> (&'a str);
    impl std::fmt::Display for UserErr<'_> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
            write!(f, "UserError: {}", self.0)
        }
    }
    impl std::error::Error for UserErr<'_> {}

   use tokio;
   use reqwest;
   #[tokio::main]
   async fn main()-> Result<(), Box<dyn std::error::Error>> {
        let client = reqwest::Client::new();
        client.get("http://google.com").send().await?;
        Err(Box::new(UserErr("error occur")))
        // Ok(())

   }

There code compile error:

   |
16 |         client.get("http://google.com").send().await?;
   |                                                     ^ the trait `From<reqwest::Error>` is not implemented for `Box<UserErr<'_>>`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following implementations were found:
             <Box<(dyn std::error::Error + 'a)> as From<E>>
             <Box<(dyn std::error::Error + 'static)> as From<&str>>
             <Box<(dyn std::error::Error + 'static)> as From<Cow<'a, str>>>
             <Box<(dyn std::error::Error + 'static)> as From<String>>
           and 22 others
   = note: required by `from`

   |
14 |    async fn main()-> Result<(), Box<dyn std::error::Error>> {
   |                      -------------------------------------- expected `Result<(), Box<(dyn std::error::Error + 'static)>>` because of return type
...
17 |         Err(Box::new(UserErr("error occur")))
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::error::Error`, found struct `UserErr`
   |
   = note: expected enum `Result<(), Box<(dyn std::error::Error + 'static)>>`
              found enum `Result<_, Box<UserErr<'_>>>`

But I have impl std::error::Error for UserErr, why this error?

When I change code to

        //Err(Box::new(UserErr("error occur")))
        Ok(())

Everythings is ok. I am confused about that.
Pls help, thanks.

Strictly speaking they aren't the same type, and Rust generally doesn't do implicit casts, so you have to cast it:

use std::error::Error;

Err( Box::new(UserErr("error occur")) as Box<dyn Error> )

I'm sure there's some more interesting type system/theory things to say about it, but I'm to ignorant...

ps: the ? operator does cast to Box<dyn Error> however, and that's by design. So you can write:

Err(Box::new( UserErr("error occur") ))?
1 Like

Cool, I see. Thank you very much.

Actually it double-box the error type. Err(UserErr("err"))? avoids it.

3 Likes

Thanks for pointing that out. Didn't realize that.

Why is double-box, can you give more detail about that. Thank you very much.

That is, dyn Error in Box<dyn Error> would be actually Box<UserErr>, not UserErr?

1 Like

The ? operator tries to convert the error you operate on into the error of the return type of the function. Apparently when the return type is Box< dyn Error > , it will do that by boxing whatever error you have, even if it is already on the heap. So it wont cast your Box< UserError > but box it again.

I suppose this is due to the available implementations of std::convert::From in the standard library. I suppose there is no From< Box<T> > for Box< dyn Error > where T: Error which uses a cast.

1 Like

Actually, this is impossible without some kind of specialization, since there's From<T> for Box<dyn Error> where T: Error, and Box<T> can implement Error itself.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.