为什么禁止`let ref a: Trait = Struct`? [英] Why is `let ref a: Trait = Struct` forbidden?

查看:37
本文介绍了为什么禁止`let ref a: Trait = Struct`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We have a noncopyable type and a trait:

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

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

If we create a &Struct and dereference it, we get an rvalue reference that we can use to initialize a by-ref binding:

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

我们也可以通过 ref 绑定直接初始化:

We can also directly initialize that by ref binding:

let ref a: Struct = Struct;

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

But if we declare our variable binding to require a reference, only the first code snippet works

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 错误.显然它们不是同一类型,但推理适用于引用.

Will give us a mismatched types error. Obviously they aren't the same type, but inference works for references.

这是根本没有实现(还没有?)还是有更深层次的原因被禁止?

Is this simply not implemented (yet?) or is there a deeper reason it's disallowed?

推荐答案

这里有一些未确定的微妙之处.

There's a little bit of unsized subtlety going on here. The key difference between

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

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

是不是表达式 *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;

通常,编译器无法知道用作类型的裸 trait 的大小,例如这里使用了 Trait.这是因为任何类型都可以实现Trait——所以trait的大小可以是任意大小,这取决于实现它的类型.所以,这就解释了为什么 let ref a: Trait = Structlet a: &Struct = &Struct;让 ref a: Trait = *a 不起作用,因为将 Struct 转换为 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.

至于为什么您的工作特征代码片段有效,查看这两个示例的 MIR,我们可以看到编译器对上述两个赋值的处理略有不同:

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;
}

我们看到编译器必须对 trait 对象进行强制转换 &'static Trait + 'static 来满足 & 的隐式强制转换.将 结构化为&Trait.从那里,引用模式很简单 var1 = &(*var0);,在这种情况下,它是从特征对象 var0 到特征对象 <代码>var1.

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.

这类似于这个函数生成的MIR:

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;
}

由于类型 [u8] 没有调整大小,它与 slice 进行了类似的转换,这在布局上与 trait 对象非常相似.最终,编译器允许代码不会引入任何未定义大小的局部变量.

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.

这篇关于为什么禁止`let ref a: Trait = Struct`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆