Hi, I'm having a bit of trouble with dynamic dispatch and the Sized trait.
The following code doesn't compile:
use std::sync::Arc;
pub trait Foo {
fn bar(self: Arc<Self>) {
self as Arc<dyn Foo>;
}
}
with the error:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:5:9
|
5 | self as Arc<dyn Foo>;
| ^^^^ doesn't have a size known at compile-time
|
= note: required for the cast to the object type `dyn Foo`
help: consider further restricting `Self`
|
4 | fn bar(self: Arc<Self>) where Self: Sized {
| +++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
IIUC Arc<Self> should implement Sized regardless of whether or not Self implements Sized because it's a pointer. My only guess is that since Self isn't necessarily Sized, Arc<Self> could be a thin or fat pointer, and the compiler doesn't know how much memory to reserve. But this is based on my very limited knowledge of dynamic dispatch and seems unlikely.
The reason it isn't possible right now has to do with how slices and trait objects are implemented. They both use metadata next to a pointer to store the dynamic information they need to work with the unsized value.
When you have a slice &[T] that gets represented as a tuple with a pointer to the start of the slice and the length of the slice (*const T, usize) . When you have a trait object &dyn Foo that gets represented as a tuple with a pointer to the start of the data for the concrete type that implements the trait and a pointer to the vtable of the functions that implement Foo for the concrete type (*const T, *const VTable)[1].
So &dyn Foo is two pointers wide, but you need another pointer's worth of space to put the length of the slice in if you want a slice to be representable as a trait object. It's not really clear where that extra data should go.
Just to mention, since both function return and function parameter are automatic coercion site you don't need the as Arc<dyn Foo> in every snippets in this thread.
Unsize<dyn Foo> + Sized is just Foo + Sized, provided Foo is dyn-safe.
You don't want that bound on your trait itself, as then dyn Foo (which is not Sized) wouldn't be able to implement Foo, and thus your entire trait is made not-dyn-safe.