Reputation: 13942
The std::error::Error
trait is transiting to a new design. Noticeably its cause
method:
fn cause(&self) -> Option<&dyn Error>
is deprecated and to be replaced by:
fn source(&self) -> Option<&(dyn Error + 'static)>
Besides the name, the only change is that the new method returns 'static
. The reason given by fix error RFC is:
The problem with the existing
cause
API is that the error it returns is not'static
. This means it is not possible to downcast the error trait object, because downcasting can only be done on'static
trait objects (for soundness reasons).
This is not obvious to me at all. Why is that?
Upvotes: 5
Views: 899
Reputation: 13942
The best way to understand it is to implement one! This SO question:
is a good starting point. The implementation in it relies on std::any::Any
but doesn't shed light on why 'static
is required. If it is to be implemented directly in the same vein as (dyn Any)::downcast_ref()
though (playground):
use std::any::TypeId;
trait A {
fn type_id(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
}
impl dyn A + 'static {
fn is<T: A + 'static>(&self) -> bool {
let t = TypeId::of::<T>();
let concrete = self.type_id();
t == concrete
}
fn downcast_ref<T: A + 'static>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(self as *const dyn A as *const T)) }
} else {
None
}
}
}
struct B;
impl A for B {}
fn main() {
let a: Box<dyn A> = Box::new(B);
let _b: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The unsafe
here is actually safe because we are only downcasting back to the original type thanks to the type id check. The implementation pivots on TypeId
to give us that guarantee:
A TypeId is currently only available for types which ascribe to 'static, but this limitation may be removed in the future.
If digging a little bit deeper, the way how this type id is calculated at the moment finally gives us the answer. That is, a struct Struct<'a>
may have N instantiations with different concrete substitutions for 'a
, but the type id is the same. Were the TypeId
to be available to all Struct<'a>
, we then can downcast any Struct<'a>
to Struct<'static>
, which would be a soundness issue.
Upvotes: 4