The following code won\'t compile due to two instances of this error:
error[E0277]: the trait bound
Self: std::marker::Sized
is not satis
Dynamically sized types can also implement traits. In particular, when you define an object-safe trait, the compiler also defines a dynamically sized type with the same name as the trait, which lets you use object types such as &Component
.
Object types such as &Component
or &Any
are not just ordinary pointers; they're fat pointers. A fat pointer combines a pointer to the data and another piece of data: for object types, it's a pointer to the vtable; for slices, it's the length of the slice.
When casting from a regular pointer (e.g. a &Button
) to an object type, the compiler statically knows which vtable to put in the fat pointer (e.g. Button
's vtable for Any
). On the other hand, Rust doesn't support casting from an object type to another object type (e.g. from &Component
to &Any
), because there's not enough data in an object to initialize the new fat pointer. This is why the compiler adds this note to the error message:
= note: required for the cast to the object type `std::any::Any + 'static`
There are two ways to fix this:
Require that all types implementing Component
be Sized
:
trait Component: Any + Sized {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
This has the consequence that you will not be able to use object types such as &Component
or Box<Component>
at all.
Make the as_any
and as_any_mut
methods only available when Self
is Sized
:
trait Component: Any {
fn as_any(&self) -> &Any
where Self: Sized
{
self
}
fn as_any_mut(&mut self) -> &mut Any
where Self: Sized
{
self
}
}
This way, you can still use object types for the trait, but you will not be able to call as_any
and as_any_mut
on them.
I found what I consider to be a great solution that didn't require new compiler features.
pub trait Component {
// ...
}
pub trait ComponentAny: Component + Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T> ComponentAny for T
where T: Component + Any
{
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
From here, I just change all my APIs to accept ComponentAny
instead of Component
. Because Any
is automatically implemented for any 'static
type, ComponentAny
is now automatically implemented for any 'static
type that implements Component
. Thanks to Is there a way to combine multiple traits in order to define a new trait? for the idea.