// this compiles
fn coerce<'a, T: Trait + Sized + 'a>(t: &T) -> &(dyn Trait + 'a) {
t
}
// this don't compile
fn coerce<'a, T: Trait + ?Sized + 'a>(t: &T) -> &(dyn Trait + 'a) {
t
}
Assume that T
is dyn Trait
, then &T
is &dyn Trait
, which exactly matches the return type.
I'm confused why Rust compiler refuses to compile the code.
References to ?Sized
types have metadata. References to trait objects also have metadata (the vtable). Therefore, ?Sized
types cannot be converted to trait objects, because there is no room for both the vtable and the original metadata.
3 Likes
This is true, but in order for coerce()
to compile, it has to work for every value of T
that meets the bounds. There are more possible types that are T: Trait + !Sized
than just dyn Trait
.
impl Trait for [i32] {}
impl Trait for dyn OtherTrait {}
struct Generic<T: ?Sized>(T);
impl<T: Trait> Trait for Generic<T> {}
trait SubTrait: Trait {}
Given these definitions, [i32]
, dyn OtherTrait
, Generic<dyn Trait>
, and dyn SubTrait
are all unsized types that implement Trait
, none of which are equal to dyn Trait
. And so all of them would need double metadata, which is impossible as @Jules-Bertholet wrote.
It would be useful to be able to write a bound which means “able to be coerced to dyn Trait
”, but there is no such bound currently, and in particular T: Trait + ?Sized
isn’t it.
3 Likes
@kpreid
Thank you so much! I understand now. You’re a wonderful teacher who can immediately identify exactly where I misunderstood.