I have a trait Cell
:
pub trait Cell: Debug + Clone + Ord {}
But when I try to create a vector of them like this:
pub struct Tuple {
pub cells: Vec<Box<dyn Cell>>,
}
The compiler gave me the following error:
error[E0038]: the trait `storage::tuple::cell::Cell` cannot be made into an object
--> src/storage/tuple/tuple.rs:20:24
|
20 | pub cells: Vec<Box<dyn Cell>>,
| ^^^^^^^^ `storage::tuple::cell::Cell` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
So I was baffled after researching for a while:
Box<dyn trait>
when the trait is "not object safe"? Given the fact that we are manipulating objects on heap, why does the compiler still not let me go?I tried:
What makes a trait "object safe", and what makes it not?
The exact rules as defined in the reference:
- All supertraits must also be object safe.
Sized
must not be a supertrait. In other words, it must not requireSelf: Sized
.- It must not have any associated constants.
- It must not have any associated types with generics.
- All associated functions must either be dispatchable from a trait object or be explicitly non-dispatchable:
- Dispatchable functions require:
- Not have any type parameters (although lifetime parameters are allowed),
- Be a method that does not use
Self
except in the type of the receiver.- Have a receiver with one of the following types:
&Self
(i.e.&self
)&mut Self
(i.e&mut self
)Box<Self>
Rc<Self>
Arc<Self>
Pin<P>
where P is one of the types above- Does not have a
where Self: Sized
bound (receiver type ofSelf
(i.e.self
) implies this).- Explicitly non-dispatchable functions require:
- Have a
where Self: Sized
bound (receiver type ofSelf
(i.e.self
) implies this).
Generally, a trait is object-safe if we can create a vtable for it (not all rules are strictly necessary, but most of them are).
Here's a summary of the reasons for the rules:
dyn Trait
for a trait with associated type, you need to specify the type of the associated type (dyn Trait<Assoc = Ty>
). This is because the compiler may need to know the concrete type when the associated type is used, e.g. in methods. There is just no way to specify the type for a generic associated type, and the semantics are also somewhat unclear.Self
, because to return a type we need to save space for it on the stack, but we don't know how big Self
is because we don't know its concrete type. It cannot take Self
as a parameter, because the semantics would be unclear: what type will the caller provide? If dyn Trait
, it could provide a type different than Self
, but the function is expecting the same type! If Self
, we don't know what it is in the caller!dyn Trait
, The receiver needs to contain a vtable, so we know what method to use. Also, when we call the method, we call it with wide pointer (data pointer + vtable). But the method itself expects a thin pointer (only the data pointer), because it knows the concrete type. The compiler needs to be able to convert between the two. The current solution is just to use a fixed set of possible types, known to the compiler (more precisely, types that implement std::ops::DispatchFromDyn
, and this trait has restrictions on what types can implement it). This may be changed in the future to allow the user to extend this list.dyn Trait
.Can we inherit an "unsized" trait and make our child trait "size/object safe" at the same time?
If by "unsized" you mean "non object-safe", then the answer is no. As said above, one of the rules for object safety is that all supertraits are object safe.
What's the usage of a "not object safe" trait?
An object safe trait can be used for dynamic polymorphism (dyn Trait
), in addition to static polymorphism (generics). Non-object-safe traits can only be used for static polymorphism. Most of our polymorphism in Rust is static, so many times this does not matter.
Why we cannot make a
Box<dyn trait>
when the trait is "not object safe"? Given the fact that we are manipulating objects on heap, why does the compiler still not let me go?
The very much definition of object safe trait is "allowed to be used in dyn Trait
". If the trait is not object-safe, we cannot create the vtable part for Box<dyn Trait>
.
So how do I fix that?
In general, you cannot. If a trait is not object-safe, it cannot be made object safe.
However, sometimes you can. For example, for Clone
, see How to clone a struct storing a boxed trait object?. Ord
is more complicated, but sometimes it is also possible, see How to test for equality between trait objects?.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With