Why it is not allowed to cast &T to &dyn Trait, where T: Trait + ?Sized?

Hello.

I observe that casting &T to &dyn Trait, where T: Trait, is allowed only if the size of T is known at compile time, that is, T: Sized.

That is, the compilation of the following code results in an error:

trait Trait {}

fn mycast<T: Trait + ?Sized>(t: &T) {
    let _: &dyn Trait = t; // the size for values of type `T` cannot be known at compilation time
}

As far as I understand, internally &dyn Trait is represented as a fat pointer, a kind of tuple consisting of (*const T, *const Vtable<Trait, T>).
I don't see why it may be technically impossible to create such a fat pointer; both the pointer to T (&T is *const T internally), and the vtable of trait Trait for type T should be known during the convertion, as both T and Trait are known at compile time.

Yet, the compiler doesn't allow such a convertion. What is the reason behind that? Is that technically impossible, or it is unsafe (if so, why ?), or some other reason?
Thanks!

&T is also a fat pointer when not Sized. The type could be a slice with the extra being the length. The code does not get to forget the length when referring to a trait object.

3 Likes

Limitation of the compiler, yet one that we have to live with. Fat pointer in Rust includes precisely two things: pointer to objects and… another pointer-sized word. This word is pointer to vtable for dyn or length for slice or something else, but “very fat pointers” are not supported.

One may imagine a different language where “very fat pointers” and, maybe, “unsized pointers” are supported… but that would be a different language, not Rust.

If T is unsized then you need to put it's size somewhere. One may imagine that compiler would create vtable on the fly and put it there (Swift does something likes this for very different reason), but, again, Rust is not designed to have hidden allocations in random places.

You would need to rethink some design decisions that Rust did to allow that.

Not impossible in some other language, but given the constraints that Rust have (no “arbitrarily fat” pointers, not hidden allocations) we are stuck with what we have.

2 Likes

The other posts here are good for why it works like this, but it made me think that we ought to have a "do the right thing" API or language feature here so that you can get a &dyn Trait to a value without having to think about whether you need &val or &&val. So I opened an IRLO thread for that: Smarter than `&&self.field` in derived debugs? - Rust Internals

2 Likes