Question about Error::source’s 'static return type

I’m a little bit confused about some details of my Error::source implementation.

My confusion is mainly about how to satisfy the Error::source signature, (&self) -> Option<&(dyn Error + 'static)>.

Here’s the playground link.

As you can see in the playground, the error type contained in MyError is not 'static (Question: should it be?). But Error::source requires that I return something 'static. Isn’t there a mismatch? Can I return something 'static if I only have something non-'static?

The casting at the bottom of the playground also makes me sort of uneasy. self.error.as_ref().map(|e| e.as_ref() as _). Is this sound at all? Why is the casting needed in items 3 and 4, but not in item 2?

Happy to hear any explanation, thank you.

From this explanation types - What does "Box<Fn() + Send + 'static>" mean in rust? - Stack Overflow,

there are some differences between &(dyn Error + 'static) and &'static(dyn Error)

1 Like

Yes, an error should be static whenever possible. (I suggest an alias like type BoxError = Box<dyn Error + Sync + Send + 'static> for convenience.)

If your error is not 'static, it means it holds references to some values on the stack. This is often not what you want because it prevents the error from bubbling past the variable that it references. (If it did, the reference would become dangling.)

2 Likes

All right, thank you, I will have to study this a bit more … it’s just odd how I’m able to return something 'static from Error::source, even though the value in my example isn’t actually marked 'static.

@troiganto You say an error should usually be 'static. When I look at std::io::Error, I see that they didn’t use 'static. Would that be historical, or would there be a reason to not use it?

That's because implicitly, dyn Trait is dyn Trait + 'static, so that is actually 'static.

Specifically, Box<dyn Trait> is implicitly Box<dyn Trait + 'static>. &dyn Trait is implicitly &'a (dyn Trait + 'a) though.

6 Likes

Thanks everybody. Just one last thing that I don’t get.

This doesn’t compile (self.error is declared as Option<Box<dyn Error + Send + Sync>>):

impl Error for MyError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.error.as_ref().map(|e| e.as_ref())
    }
}
error[E0308]: mismatched types
  --> src/main.rs:27:9
   |
27 |         self.error.as_ref().map(|e| e.as_ref())
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::error::Error`, found trait `std::error::Error + std::marker::Send + std::marker::Sync`
   |
   = note: expected type `std::option::Option<&(dyn std::error::Error + 'static)>`
              found type `std::option::Option<&dyn std::error::Error + std::marker::Send + std::marker::Sync>`

However, just assigning to a temporary variable of type &dyn Error makes the code compile:

impl Error for MyError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.error.as_ref().map(|e| {
            let e: &dyn Error = e.as_ref();
            e
        })
    }
}

Why do I have to get rid of the traits Send and Sync before I can return the error object? Why isn't the type &(dyn Error + Send + Sync + 'static) compatible with the return type &(dyn Error + 'static)? Thanks again.

1 Like

Hmm, getting the inference to work across closures is sometimes finnicky in my experience; try

self.error.as_ref().map(|e| e.as_ref() as &_)

Or even maybe,

self.error.as_ref().map::<_>(|e| e.as_ref() as &_)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.