I have a linked list of errors which I'm trying to traverse.
Given MyError
, which is a linked list of errors with an optional code, my goal is to traverse the chain and return the first non-None
code:
type BoxedError = Box<dyn std::error::Error + Send + Sync + 'static>;
#[derive(Debug)]
struct MyError {
code: Option<u32>,
source: Option<BoxedError>,
}
impl std::error::Error for MyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source().map(|s| s as _)
}
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt("", f)
}
}
impl MyError {
fn source(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
self.source.as_ref().map(|c| c.as_ref())
}
fn traverse(&self) -> Option<u32> {
use core::any::Any;
if let Some(code) = self.code {
return Some(code);
}
if let Some(source) = self.source() {
let as_any = &source as &dyn Any;
if let Some(myerr) = as_any.downcast_ref::<MyError>() {
return myerr.traverse();
}
}
None
}
}
This gives the following error:
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:31:36
|
26 | fn traverse(&self) -> Option<u32> {
| ----- this data with an anonymous lifetime `'_`...
...
31 | if let Some(source) = self.source() {
| ---- ^^^^^^
| |
| ...is used here...
32 | let as_any = &source as &dyn Any;
| ------- ...and is required to live as long as `'static` here
For more information about this error, try `rustc --explain E0759`.
error: could not compile `playground` due to previous error
I'm not sure why I'd have a lifetime error here given that the source function returns a static trait object (which means that there are no other references behind that object, right?).
&source as &dyn Any
doesn't do what you think. source
is of type &(dyn std::error::Error + Send + Sync + 'static)
, so this is the type that must be erased to coerce &source
to &dyn Any
.
The + 'static
in this type only means that the referenced type, the one implementing Error + Send + Sync
, must contain no non-'static
references. The &
still has its own lifetime, which is not 'static
(and cannot be, since it is borrowed from self
). It is the lifetime of this reference which prevents &(dyn std::error::Error + Send + Sync + 'static)
from being 'static
.
What you meant to do was source as &dyn Any
, which, if it worked, would upcast the dyn Error
to dyn Any
. But this is not possible because Any
is not a supertrait of Error
. Moreover, even if Error
did have Any
as a supertrait, Rust doesn't currently support such a cast.
Trait upcasting is available on nightly under a feature flag, but you don't need that. Use the downcast
methods defined on dyn Error
itself, which are added to support this use case.
if let Some(myerr) = source.downcast_ref::<MyError>() {
return myerr.traverse();
}
If you need to downcast a trait object that is not dyn Error
, you will need to find a workaround.