rust - 为什么 `let ref a: Trait = Struct` 被禁止?

标签 rust

我们有一个不可复制的类型和一个特征:

struct Struct;
trait Trait {}
impl Trait for Struct {}

如果我们创建一个 &Struct 并取消引用它,我们会得到一个右值引用,我们可以使用它来初始化一个 by-ref 绑定(bind):

let a: &Struct = &Struct;
let ref a: Struct = *a;

我们也可以直接通过 ref 绑定(bind)来初始化它:

let ref a: Struct = Struct;

但是如果我们声明我们的变量绑定(bind)需要引用,只有第一个代码片段有效

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;

会给我们一个不匹配的类型错误。显然它们不是同一类型,但推理适用于引用。

这是根本没有实现(还没有?)还是有更深层次的原因不允许它?

最佳答案

这里有一些微妙之处。

之间的主要区别
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 = Structlet a: &Struct = &Struct; let ref a: Trait = *a不工作,因为转换 StructTrait是一个未定尺寸的铸件。

至于为什么你的 working 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 .从那里开始,引用模式就是 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]没有大小,它对 slice 进行类似的转换,这在布局上与特征对象非常相似。最终,编译器允许不引入任何未确定大小的局部变量的代码。

关于rust - 为什么 `let ref a: Trait = Struct` 被禁止?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36057645/

相关文章:

generics - 如何实现采用特征MyTrait <A>的结构? [复制]

php - 为什么我没有收到 FCGI_END_REQUEST 记录?

rust - 匹配子模式中的或运算符

rust - 如何为元组结构成员分配默认值

rust - 我如何存储枚举以便仅通过识别变体来检索它?

rust - 惯用地访问可变和不可变向量的元素

module - 包含来自另一个既不是 main.rs 也不是 lib.rs 的文件

rust - 为什么在链表上调用 into_iter 时 self 不移动?

rust - Rocket CORS如何使用Request Guard返回字符串?

rust - 在 Rust 程序中同时使用 libnative 和 libgreen