在涉及到drop顺序时,对于变量的阴影和重新分配(覆盖)之间存在差异。
所有局部变量通常在作用域结束时按照声明的相反顺序被删除(请参见The Rust Programming Language的Drop
章节)。这包括被遮蔽的变量。可以通过将值包装在一个简单的包装结构中来轻松检查此内容,当该包装结构被删除时(就在值本身被删除之前),它会打印出一些东西:
use std::fmt::Debug;
struct NoisyDrop<T: Debug>(T);
impl<T: Debug> Drop for NoisyDrop<T> {
fn drop(&mut self) {
println!("dropping {:?}", self.0);
}
}
fn main() {
let hello = NoisyDrop("Hello");
let hello = NoisyDrop("Goodbye");
println!("My variable hello contains: {}", hello.0);
}
打印以下内容(
playground):
My variable hello contains: Goodbye
dropping "Goodbye"
dropping "Hello"
这是因为作用域中的新let
绑定不会覆盖先前的绑定,所以就好像你写了
let hello1 = NoisyDrop("Hello");
let hello2 = NoisyDrop("Goodbye");
println!("My variable hello contains: {}", hello2.0);
请注意,这个行为与下面的代码(playground)表面上非常相似,但实际上是不同的:
fn main() {
let mut hello = NoisyDrop("Hello");
hello = NoisyDrop("Goodbye");
println!("My variable hello contains: {}", hello.0);
}
这段代码不仅将值按相反顺序丢弃,而且在打印消息之前丢弃第一个值!这是因为当您将变量赋值给一个变量(而不是使用新变量隐藏它)时,原始值会在新值移入之前被丢弃。
我开始说局部变量在超出范围时“通常”会被删除。因为您可以将值移入和移出变量,所以有时候需要在运行时才能确定何时需要删除变量,此时编译器实际上插入代码来跟踪“活动状态”,并在必要时删除这些值,因此您不能通过覆盖值而意外地导致泄漏。(但是,仍然可能通过调用mem::forget
或创建具有内部可变性的Rc
-循环来安全地泄漏内存。)
另请参阅