临时变量在语句的末尾被删除,就像在 C++ 中一样。然而,IIRC,Rust 中的破坏顺序是未指定的(我们将在下面看到它的后果),尽管当前的实现似乎只是以相反的构造顺序删除值。
let _ = x; 和 let _b = x; 之间存在很大差异。 _ 不是 Rust 中的标识符:它是通配符模式。由于此模式没有找到任何变量,因此最终值在语句末尾被有效地删除。
另一方面,_b 是一个标识符,因此该值绑定到具有该名称的变量,这将其生命周期延长到函数结束。但是,A 实例仍然是临时的,所以它会在语句末尾被删除(我相信 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 B 打印在End 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 无法推断这些对象的销毁顺序。错误来自这样一个事实,即当 B<'a> 实现 Drop 时,Rust 拒绝用对象本身的生命周期实例化 B<'a>(此规则是在 Rust 1.0 之前作为 RFC 769 的结果添加的)。如果允许,drop 将能够访问已删除的值!但是,如果B<'a> 没有实现Drop,那么它是允许的,因为我们知道当结构体被删除时,没有代码会尝试访问B 的字段。