声明结束时临时删除,就像在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>
与对象本身的生命周期,当B<'a>
实现了Drop
(该规则在Rust 1.0之前被添加为RFC 769的结果)。如果允许,drop
将能够访问已经被删除的值!但是,如果B<'a>
没有实现Drop
,那么它是允许的,因为我们知道当结构被删除时没有代码会尝试访问B
的字段。
这里有一些讨论,但仍然没有详细说明我相信虽然比以前更一致:https://github.com/rust-lang/rust/issues/32433 – WiSaGaN