We have a noncopyable type and a trait:
struct Struct;
trait Trait {}
impl Trait for Struct {}
If we create a &Struct
and
There's a little bit of unsized subtlety going on here. The key difference between
let a: &Struct = &Struct;
let ref a: Struct = *a;
and
let a: &Trait = &Struct;
let ref a: Trait = *a;
Is that the expression *a
produces a value whose size is not known at compile-time. This manifests as an error when we attempt to do:
let ref a: Trait = Struct as Trait;
<anon>:6:24: 6:39 error: cast to unsized type: `Struct` as `Trait`
<anon>:6 let ref a: Trait = Struct as Trait;
^~~~~~~~~~~~~~~
<anon>:6:24: 6:30 help: consider using a box or reference as appropriate
<anon>:6 let ref a: Trait = Struct as Trait;
In general, the compiler can't know the size of a bare trait used as a type, like Trait
is used here. This is because any type can implement Trait
- so the size of trait can be any size, depending on the type that implements it. So, that explains why let ref a: Trait = Struct
and let a: &Struct = &Struct; let ref a: Trait = *a
don't work, because casting a Struct
to a Trait
is an unsized cast.
As for why your working trait code snippet works, looking at the MIR for these two examples, we can see that the compiler is treating the two above assignments slightly differently:
let a: &Struct = &Struct;
let ref a: Struct = *a;
bb0: {
tmp1 = Struct;
tmp0 = &tmp1;
var0 = &(*tmp0);
var1 = &(*var0);
return = ();
goto -> bb1;
}
let a: &Trait = &Struct;
let ref a: Trait = *a;
bb0: {
tmp2 = Struct;
tmp1 = &tmp2;
tmp0 = &(*tmp1);
var0 = tmp0 as &'static Trait + 'static (Unsize);
var1 = &(*var0);
return = ();
goto -> bb1;
}
We see that the compiler has to do a cast to a trait object &'static Trait + 'static
to satisfy the implicit coercion of &Struct
to &Trait
. From there, the ref pattern is simply var1 = &(*var0);
, which in this case is a simple assignment from the trait object var0
to the trait object var1
.
This is similar to the MIR generated by this function:
fn stuff() {
let sized = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
let slice : &[u8] = &sized;
let ref other_slice = *slice;
}
bb0: {
var0 = [const 1u8, ..., const 0u8];
tmp2 = &var0;
tmp1 = &(*tmp2);
var1 = tmp1 as &'static [u8] (Unsize);
var2 = &(*var1);
return = ();
goto -> bb1;
}
Since the type [u8]
is unsized, it does a similar cast to a slice, which is quite similar in layout to a trait object. Ultimately, the compiler allows the code that doesn't introduce any unsized locals.