I am new to rust and trying to wrap my head around this error. If i use write this code
pub struct A {}
pub trait Trait {
fn stuff(self: Box<Self>) -> Box<dyn Trait>;
}
impl Trait for A {
fn stuff(self: Box<Self>) -> Box<dyn Trait> {
self
}
}
It compiles fine.
But if i write this
pub struct A {}
pub trait Trait {
fn stuff(self: Box<Self>) -> Box<dyn Trait> {
self
}
}
impl Trait for A {}
I get error
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:5:9
|
5 | self
| ^^^^ doesn't have a size known at compile-time
|
= note: required for the cast from `Box<Self>` to `Box<(dyn Trait + 'static)>`
help: consider further restricting `Self`
|
4 | fn stuff(self: Box<Self>) -> Box<dyn Trait> where Self: Sized {
| +++++++++++++++++
Why must self be sized when implemented as default but not when implemented in the struct? In both cases, i am using Box pointer so size is not really needed since no matter what goes in the type argument, Box will always have the same size.
You can provide a default implementation returning Box<Self>
as Box<dyn Trait>
by constraining Self
to be Sized
and 'static
:
pub trait Trait {
fn stuff(self: Box<Self>) -> Box<dyn Trait>
where
Self: Sized + 'static,
{
self
}
}
It requires 'static
because that is the inferred lifetime of Box<dyn Trait>
. There is no lifetime (elided or otherwise) in the method parameters that a shorter one could be derived from anyway. Self
could have lifetimes associated with it, but they can't affect the trait signature, so therefore Self
must adhere to the trait constraints.
It requires Sized
due to how trait objects like Box<dyn Trait>
are implemented. If you have a boxed sized type (Box<T>
) then that is represented by a single pointer. If you have a boxed unsized type (Box<[T]>
, Box<dyn Trait>
, Box<str>
, etc.) then it is represented by two pointers: one to the value and another for metadata (a v-table for trait objects or a length for slices). Also known as a "fat pointer".
If Self
were not Sized
and you tried to add another level of unsized-ness to it by coercing it into a Box<dyn Trait>
, then either it would need to be a "fat fat pointer" which would be incoherent since Box<dyn Trait>
itself could have two different sizes, or it would need to lose a bit of metadata which of course Rust isn't going to do.
See Why can't `&(?Sized + Trait)` be cast to `&dyn Trait`? for a more in-depth explanation.