For code below rustc reports:
| let foo: &dyn Foo = slice;
| ^^^^^ doesn't have a size known at compile-time
but size of slice is obviously known (pointer + length), so what is real problem with my code?
use std::any::Any;
trait Foo {
fn len(&self) -> usize;
fn get(&self, index: usize) -> Option<&dyn Any>;
}
impl<T> Foo for [T]
where
T: 'static,
{
fn len(&self) -> usize {
self.len()
}
fn get(&self, index: usize) -> Option<&dyn Any> {
<[T]>::get(self, index).map(|v| v as &dyn Any)
}
}
fn main() {
let v = Vec::<i32>::new();
let slice = v.as_slice();
let foo: &dyn Foo = slice;
}
Rust would have to store both the vtable for [Foo] and the length of the slice in foo, and there’s no room because fat pointers can’t be that fat. You need double indirection I think.
4 Likes
There's some unfortunate terminology confusion.
Strictly speaking, a slice is an (unsized) value of type [T] - in your case, [i32]. Usually, it is used behind the reference, so in most cases term "slice" really means "reference to a slice" - as it is in this quote.
But here, you have impl<T> Foo for [T] - that is, for actual slice, not for reference. Therefore, you've tried to convert &[T] into &dyn Foo - that is, [T] into dyn Foo; and in this conversion, size of a source type is not known.
3 Likes
But why rust need to convert &[T] to [T] to Foo and then to &dyn Foo, can it convert directly &[T] to &dyn Foo? For methods like fn len(&self) -> usize that implemented for slice, rustc not convert type to [T] to call method it just work directly with &[T].
Rust does not perform all that conversion chain. The conversion would be directly from &YourType to &dyn Foo, or more in general something that points to YourType to the same something but now pointing to dyn Foo). However for it to do this it needs YourType to not require additional metadata, and the way this is currently approximated is by requiring it to implement the Sized trait, i.e. have a compile-time known size. The reason it cannot have additional metadata is that there's no place for it in the &dyn Foo or any other type pointing to dyn Foo. A [T] does need additional metadata for its length, hence why you can't make this conversion.
3 Likes
Here's what a conversion looks like for a sized type.
&SomeType SomeType
+------+ +------+------+
| dptr | --> | Some | Type |
+------+ +------+------+
# ... coerce &SomeType to &dyn Foo (or Box<SomeType> to Box<dyn Foo>) ...
&dyn Foo SomeType Vtable for SomeType
+------+------+ +------+------+ +------+
| vptr | dptr | --> | Some | Type | | Size |
+------+------+ +------+------+ +------+
| | fptr | // fn(*const ()) for len
+-------------------------------------> +------+ // ... etc ...
And it looks similar for every other sized type too. After the coercion,
- There's no way to be sure of the original type (e.g.
SomeType) from the &dyn Foo
- The
&dyn Foo is the "same shape" (vtable pointer, data pointer) no matter what the original type was
- The vtable is also the same shape (e.g. same set of erased function pointers)
- The size of the erased type is only known via the vtable, which is static and read-only
- The dispatch to trait methods happens the same way for every erased type, using the "thin" pointer (no metadata) to the data and the function pointer in the vtable
This is incompatible with converting a &[T] or Box<[T]>, etc...
&[T] [T]
+------+------+ +------+------+----
| Len | dptr | --> | data | data | .........
+------+------+ +------+------+----
For a number of reasons, e.g.
- There's no place for the
Len to go
&dyn Foo is the size of two pointers, not two pointers and a length, so it can't be part of the wide pointer
- The length is dynamic, but vtables are static, so it can't go in the vtable
- The dispatch would be different (a wide
*const [()] would need to be created)
As others pointed out, the pointer + length is a &[T] (or Box<[T]> etc), not [T]. If you implement Foo for those, you can convert a &&[T] or &Box<[T]> to &dyn Foo. (Playground.)
&&[T] &[T] [T]
+------+ +------+------+ +------+------+----
| dptr | --> | Len | dptr | --> | data | data | .........
+------+ +------+------+ +------+------+----
# ... coercion ...
&dyn Foo &[T] [T]
+------+------+ +------+------+ +------+------+----
| vptr | dptr | --> | Len | dptr | --> | data | data | .........
+------+------+ +------+------+ +------+------+----
|
+----> ...
2 Likes