Implementing Error::source using internally stored dyn Error + Send

Hello.

I have an error type that internally stores a lower-level error as a Option<Box<dyn Error + Send + 'static>>. I am looking to implement the Error trait for it. It works when I just try to add an empty impl (i.e. leaving the trait methods as their defaults), but if I try to override the source method to return the inner error I run into trouble:

#[derive(Debug)]
struct MyError(Option<Box<dyn Error + Send + 'static>>);

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "my error")
    }
}

impl Error for MyError {
    // Fails to compile, but works if I remove the Send bound from the trait object in MyError
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.0.as_ref().map(|v| &**v)
    }
}

(playground)

The error:

error[E0308]: mismatched types
  --> src/lib.rs:16:9
   |
16 |         self.0.as_ref().map(|v| &**v)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::error::Error`, found trait `std::error::Error + Send`
   |
   = note: expected enum `Option<&(dyn std::error::Error + 'static)>`
              found enum `Option<&dyn std::error::Error + Send>`

Is there a way to resolve this?

You can use an explicit cast:

fn source(&self) -> Option<&(dyn Error + 'static)> {
    self.0.as_ref().map(|v| &**v as &(dyn Error + 'static))
}

Playground

2 Likes

Thanks! I had just been linked to a duplicate of this thread at Question about Error::source’s 'static return type :slight_smile:

It would be cool if the error message hinted at this, because it definitely did not occur to me to use an as cast.

1 Like

You can also replace the &**v dance with Option::as_deref:

fn source(&self) -> Option<&(dyn Error + 'static)> {
    self.0.as_deref().map(|v| v as &(dyn Error + 'static))
}
3 Likes

Using a match is not wrong :slight_smile:

impl Error for MyError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self.0 {
            Some(ref v) => Some(&**v),
            None => None,
        }
    }
}

If you want conciseness, this also works:

fn source(&self) -> Option<&(dyn Error + 'static)> {
    Some(self.0.as_deref()?)
}
1 Like