Rough sketch, I'd like to generalize the backtrace
fn to instead return arbitrary types.
Example
Heres what the trait and impls might look like
#![feature(backtrace)]
use std::any::{Any, TypeId};
use std::backtrace::Backtrace;
pub trait Error {
fn context_raw(&self, id: TypeId) -> Option<&dyn Any>;
}
impl dyn Error {
pub fn context<T: Any>(&self) -> Option<&T> {
self.context_raw(TypeId::of::<T>())?.downcast_ref::<T>()
}
}
struct Blah {
context: Backtrace,
more_context: String,
}
impl Error for Blah {
fn context_raw(&self, id: TypeId) -> Option<&dyn Any> {
if id == TypeId::of::<Backtrace>() {
Some(&self.context)
} else if id == TypeId::of::<String>() {
Some(&self.more_context)
} else {
None
}
}
}
And using it would look like this
fn test_context() {
let obj = Blah {
context: Backtrace::capture(),
more_context: "There is something you should really know about this error...".into(),
};
let traitobj: Box<dyn Error> = Box::new(obj);
let context: Option<&Backtrace> = traitobj.context();
let more_context: Option<&String> = traitobj.context();
if let Some(context) = context {
println!("{}", context);
}
if let Some(context) = more_context {
println!("{}", context);
}
}
Motivation
The main motiviation for this is that I'd like to be able to extract tracing_error::SpanTrace
s from &dyn Error
trait objects, which are analogous to backtraces. AFAIK doing this with backtrace
was the original motivation for the Fail
trait, so this seems like an extension of previous work.
Another benefit is that it may make the error trait easier to move out of std
into either alloc
or core
in the future. I'm told that backtrace already supports no_std so this is a nonissue Edit: apparently backtrace is still an issue for moving the Error trait to alloc based on this issue but now I'm confused as to why...
Issues
I imagine that renaming the backtrace fn, even though its technically unstable, would be a pretty big change ecosystem wide. It might make more sense to add the context
method on the side rather than replacing the backtrace
method entirely, though I worry that this will interfere with moving the error::Error trait out of std in the future.
Also, the context
fn identifier is relatively well used throughout the error handling ecosystem, namely anyhow and snafu, though afaik they mostly use it as a helper for map_err!
to wrap an inner error type. This would also cause friction with the ecosystem so it maybe desirable to pick a different name than context
.
Overall this is definitely a usability regression in terms of working with the error trait, now you have to work with type_id's and the Any trait which may make working with the error trait even more confusing for beginners.