rust - Rust 中涉及临时对象的销毁顺序

标签 rust temporary-objects

在 C++ 中(如果错误请纠正我),通过常量引用的临时绑定(bind)应该比它绑定(bind)到的表达式更有效。我假设在 Rust 中也是如此,但我在两种不同的情况下得到了两种不同的行为。

考虑:

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.

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

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

输出是:

Drop A.
Drop B.

这不是我所期望的。

这是有意为之吗?如果是这样,那么这两种情况下行为差异的基本原理是什么?

我正在使用 Rust 1.12.1。

最佳答案

临时变量在语句末尾被删除,就像在 C++ 中一样。然而,IIRC,Rust 中的销毁顺序是未指定的(我们将在下面看到它的后果),尽管当前的实现似乎只是简单地以相反的构造顺序丢弃值。

let _ = x; 之间有很大区别和 let _b = x; . _不是 Rust 中的标识符:它是一个通配符模式。由于此模式未找到任何变量,最终值实际上被丢弃在语句末尾。

另一方面,_b是一个标识符,因此该值被绑定(bind)到一个具有该名称的变量,这会延长其生命周期直到函数结束。然而,A instance 仍然是临时的,所以它会在语句结束时被删除(我相信 C++ 也会这样做)。由于语句结束在函数结束之前,A实例首先被丢弃,B第二个实例被丢弃。

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

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

这会产生以下输出:

Drop B.
Drop A.
End of main.

到目前为止一切顺利。现在让我们试试 let _b ;输出是:

Drop A.
End of main.
Drop B.

正如我们所见,Drop BEnd of main. 之后打印.这表明 B实例一直存活到函数结束,解释了不同的销毁顺序。


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

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

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

这编译得很好。在幕后,Rust 为 A 分配了相同的生命周期。实例和 B实例(即,如果我们引用了 B 实例,它的类型将是 &'a B<'a>,其中 'a 的生命周期完全相同)。当两个值具有相同的生命周期时,那么我们必然需要先删除其中一个,并且如上所述,顺序是未指定的。如果我们加回 Drop 会发生什么实现?

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

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

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 无法推断出这些对象的销毁顺序。该错误来自 Rust 拒绝实例化 B<'a> 的事实当 B<'a> 时对象本身的生命周期工具 Drop (此规则是 Rust 1.0 之前的 RFC 769 的结果)。如果允许,drop将能够访问已经被删除的值!但是,如果 B<'a>没有实现 Drop , 那么它是允许的,因为我们知道没有代码会尝试访问 B删除结构时的字段。

关于rust - Rust 中涉及临时对象的销毁顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40289424/

相关文章:

rust - 可变和不可变数据访问

c++ - 临时对象创建

c++ - 临时对象的生命周期 : iterator to temporary vector in nested function call

c++ - 为什么不是对临时对象的非常量引用?

rust - 在 libp2p 中实现对等点发现

rust ...为什么要将函数从 rs 文件导入到库中? - 未能解决 : use of undeclared type or module `client`

c++ - 临时对象的取消引用运算符

c++ - 为什么 const 引用不能延长通过函数传递的临时对象的生命周期?

rust - 在对其进行模式匹配时修改结构的字段

rust - 为什么盒装变量需要显式类型才能传递给函数?