这里有一点不为人知的微妙之处。
之间的主要区别
let a: &Struct = &Struct;
let ref a: Struct = *a;
和
let a: &Trait = &Struct;
let ref a: Trait = *a;
是不是表达式*a 产生了一个在编译时大小未知的值。当我们尝试这样做时,这表现为错误:
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;
一般而言,编译器无法知道用作类型的裸特征的大小,例如此处使用Trait。这是因为 any type 可以实现 Trait - 所以 trait 的大小可以是 any size,具体取决于实现它的类型。所以,这就解释了为什么 let ref a: Trait = Struct 和 let a: &Struct = &Struct; let ref a: Trait = *a 不起作用,因为将 Struct 转换为 Trait 是一个未调整大小的转换。
至于为什么您的工作特征代码 sn-p 有效,查看这两个示例的 MIR,我们可以看到编译器对上述两个赋值的处理略有不同:
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;
}
我们看到编译器必须对 trait 对象 &'static Trait + 'static 进行强制转换,以满足 &Struct 到 &Trait 的隐式强制转换。从那里开始,引用模式就是 var1 = &(*var0);,在这种情况下,它是从 trait 对象 var0 到 trait 对象 var1 的简单赋值。
这类似于这个函数生成的MIR:
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;
}
由于[u8] 类型没有大小,它与slice 进行类似的强制转换,这在布局上与特征对象非常相似。最终,编译器允许代码不会引入任何未调整大小的局部变量。