我们有一个不可复制的类型和特征:
struct Struct;
trait Trait {}
impl Trait for Struct {}
如果我们创建一个&Struct
并取消引用它,我们会得到一个rvalue引用,我们可以用它来初始化一个by-ref绑定:
let a: &Struct = &Struct;
let ref a: Struct = *a;
我们也可以通过ref binding来直接初始化它:
let ref a: Struct = Struct;
但是如果我们声明我们的变量绑定需要引用,那么只有第一个代码片段可以工作
let a: &Trait = &Struct;
let ref a: Trait = *a;
试图直接这样做
let ref a: Trait = Struct;
或通过循环
let a: &Struct = &Struct;
let ref a: Trait = *a;
或者
let ref a: Trait = *&Struct;
会给我们一个mismatched types
错误。显然它们不是同一类型,但推理适用于参考文献。
这根本没有实现(但是?)还是有更深层次的理由被禁止?
答案 0 :(得分:4)
这里有一点点未经证实的微妙之处。
之间的关键区别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
。这是因为任何类型都可以实现Trait
- 因此特征的大小可以是任何大小,具体取决于实现它的类型。因此,这解释了为什么let ref a: Trait = Struct
和let a: &Struct = &Struct; let ref a: Trait = *a
无法正常工作,因为将Struct
投放到Trait
是一个未实现的演员。
至于为什么你的工作特征代码片段工作,看看这两个例子的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;
}
我们看到编译器必须对特征对象 &'static Trait + 'static
进行强制转换,以满足&Struct
到&Trait
的隐式强制。从那里开始,ref模式只是var1 = &(*var0);
,在这种情况下,它是从特征对象var0
到特征对象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]
未归类,因此它对切片执行类似的转换,这在布局上与特征对象非常相似。最终,编译器允许代码不会引入任何未定义的本地化。