Rust中涉及临时人员的销毁顺序 [英] Destruction order involving temporaries in Rust

查看:125
本文介绍了Rust中涉及临时人员的销毁顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中(如果错了,请纠正我),通过常量引用进行的临时绑定应该比它绑定的表达式的寿命更长.我以为在Rust中也是如此,但是在两种不同的情况下,我会得到两种不同的行为.

In C++ (please correct me if wrong), a temporary bound via constant reference is supposed to outlive the expression it is bound to. I assumed the same was true in Rust, but I get two different behaviors in two different cases.

考虑:

struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }

struct B(*const A);
impl Drop for B { fn drop(&mut self) { println!("Drop B.") } }

fn main() {
    let _ = B(&A as *const A); // B is destroyed after this expression itself.
}

输出为:

Drop B.
Drop A.

这就是您所期望的.但是现在,如果您这样做:

This is what you would expect. But now if you do:

fn main() {
    let _b = B(&A as *const A); // _b will be dropped when scope exits main()
}

输出为:

Drop A.
Drop B.

这不是我期望的.

这是故意的吗?如果是,那么两种情况下行为差异的理由是什么?

Is this intended and if so then what is the rationale for the difference in behavior in the two cases?

我正在使用Rust 1.12.1.

I am using Rust 1.12.1.

推荐答案

临时语句在语句末尾删除,就像在C ++中一样.但是,对于IIRC,Rust中的销毁顺序是不确定的(我们将在下面看到其后果),尽管当前实现似乎只是按相反的构造顺序删除值.

Temporaries are dropped at the end of the statement, just like in C++. However, IIRC, the order of destruction in Rust is unspecified (we'll see the consequences of this below), though the current implementation seems to simply drop values in reverse order of construction.

let _ = x;let _b = x;之间有很大的区别. _不是Rust中的标识符:这是一个通配符模式.由于此模式找不到任何变量,因此最终值将在语句末尾有效地删除.

There's a big difference between let _ = x; and let _b = x;. _ isn't an identifier in Rust: it's a wildcard pattern. Since this pattern doesn't find any variables, the final value is effectively dropped at the end of the statement.

另一方面,_b是一个标识符,因此该值将绑定到具有该名称的变量,从而将其寿命延长到函数结束.但是,A实例仍然是临时的,因此它将在语句末尾删除(并且我相信C ++会这样做).由于语句的末尾在函数的末尾之前,因此先删除A实例,然后再删除B实例.

On the other hand, _b is an identifier, so the value is bound to a variable with that name, which extends its lifetime until the end of the function. However, the A instance is still a temporary, so it will be dropped at the end of the statement (and I believe C++ would do the same). Since the end of the statement comes before the end of the function, the A instance is dropped first, and the B instance is dropped second.

为了更清楚一点,让我们在main中添加另一条语句:

To make this clearer, let's add another statement in main:

fn main() {
    let _ = B(&A as *const A);
    println!("End of main.");
}

这将产生以下输出:

Drop B.
Drop A.
End of main.

到目前为止,一切都很好.现在让我们尝试let _b;输出为:

So far so good. Now let's try with let _b; the output is:

Drop A.
End of main.
Drop B.

如我们所见,在End of main.之后打印Drop B.这表明B实例在该函数结束之前还活着,说明了不同的销毁顺序.

As we can see, Drop B is printed after End of main.. This demonstrates that the B instance is alive until the end of the function, explaining the different destruction order.

现在,让我们看看如果修改B以采用具有生存期的借用指针而不是原始指针会发生什么.实际上,让我们更进一步,暂时删除Drop实现:

Now, let's see what happens if we modify B to take a borrowed pointer with a lifetime instead of a raw pointer. Actually, let's go a step further and remove the Drop implementations for a moment:

struct A;
struct B<'a>(&'a A);

fn main() {
    let _ = B(&A);
}

这可以编译.在幕后,Rust为A实例和B实例分配相同的生存期(即,如果我们引用B实例,则其类型为&'a B<'a>,其中两个'a均为完全一样的寿命).当两个值具有相同的生存期时,则必然需要我们将其中一个值放在另一个值之前,并且如上所述,该顺序是不确定的.如果我们重新添加Drop实现,会发生什么?

This compiles fine. Behind the scenes, Rust assigns the same lifetime to both the A instance and the B instance (i.e. if we took a reference to the B instance, its type would be &'a B<'a> where both 'a are the exact same lifetime). When two values have the same lifetime, then necessarily we need to drop one of them before the other, and as mentioned above, the order is unspecified. What happens if we add back the Drop implementations?

struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }

struct B<'a>(&'a A);
impl<'a> Drop for B<'a> { fn drop(&mut self) { println!("Drop B.") } }

fn main() {
    let _ = B(&A);
}

现在我们遇到了编译器错误:

Now we're getting a compiler error:

error: borrowed value does not live long enough
 --> <anon>:8:16
  |
8 |     let _ = B(&A);
  |                ^ does not live long enough
  |
note: reference must be valid for the destruction scope surrounding statement at 8:4...
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^
note: ...but borrowed value is only valid for the statement at 8:4
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^
help: consider using a `let` binding to increase its lifetime
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^

由于已为A实例和B实例分配了相同的生存期,因此Rust无法推断这些对象的销毁顺序.错误来自以下事实:当B<'a>实现Drop时,Rust拒绝使用对象本身的生存期实例化B<'a>(此规则是

Since both the A instance and the B instance have been assigned the same lifetime, Rust cannot reason about the destruction order of these objects. The error comes from the fact that Rust refuses to instantiate B<'a> with the lifetime of the object itself when B<'a> implements Drop (this rule was added as the result of RFC 769 before Rust 1.0). If it was allowed, drop would be able to access values that have already been dropped! However, if B<'a> doesn't implement Drop, then it's allowed, because we know that no code will try to access B's fields when the struct is dropped.

这篇关于Rust中涉及临时人员的销毁顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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