5.2 Drop 标志

Drop 标志如何跟踪初始化

译文 · 基于 The Rustonomicon

Drop 标志

原文链接: https://doc.rust-lang.org/nomicon/drop-flags.html

  上一节的例子为 Rust 引入了一个有趣问题。   我们已经看到可以有条件地初始化、去初始化和重新初始化内存位置,且完全安全。对 Copy 类型这不特别值得注意,因为它们只是随机比特堆。但有析构函数的类型不同:Rust 需要知道变量被赋值或变量离开作用域时是否应调用析构函数。   在有条件初始化的情况下如何做到?

  注意并非所有赋值都需要担心这一点。   特别是,通过解引用赋值会无条件 drop,在 let 中赋值则无条件不 drop:

1
2
3
let mut x = Box::new(0); // let 创建新变量,因此从不需要 drop
let y = &mut x;
*y = Box::new(1); // Deref 假设被引用对象已初始化,因此总是 drop

  这只在覆盖先前已初始化的变量或其子字段时才是问题。

  Rust 实际上在运行时追踪类型是否应被 drop。变量随初始化/未初始化切换,该变量的 drop 标志随之切换。当变量可能需要 drop 时,评估此标志决定是否 drop。

  当然,值的初始化状态在程序每一点常可静态已知。若如此,编译器理论上可生成更高效代码!例如直线代码具有此类静态 drop 语义:

1
2
3
4
5
6
let mut x = Box::new(0); // x 未初始化;直接覆盖。
let mut y = x;           // y 未初始化;直接覆盖并使 x 未初始化。
x = Box::new(0);         // x 未初始化;直接覆盖。
y = x;                   // y 已初始化;Drop y,覆盖它,并使 x 未初始化!
                         // y 离开作用域;y 已初始化;Drop y!
                         // x 离开作用域;x 未初始化;什么都不做。

  同样,所有分支对初始化行为一致的分支代码具有静态 drop 语义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# let condition = true;
let mut x = Box::new(0);    // x 未初始化;直接覆盖。
if condition {
    drop(x)                 // x 被 move 出;使 x 未初始化。
} else {
    println!("{}", x);
    drop(x)                 // x 被 move 出;使 x 未初始化。
}
x = Box::new(0);            // x 未初始化;直接覆盖。
                            // x 离开作用域;x 已初始化;Drop x!

  但如下代码需要运行时信息才能正确 Drop:

1
2
3
4
5
6
7
8
# let condition = true;
let x;
if condition {
    x = Box::new(0);        // x 未初始化;直接覆盖。
    println!("{}", x);
}
                            // x 离开作用域;x 可能未初始化;
                            // 检查标志!

  当然,此例可轻易恢复静态 drop 语义:

1
2
3
4
5
# let condition = true;
if condition {
    let x = Box::new(0);
    println!("{}", x);
}

  drop 标志在栈上追踪。   旧版 Rust 中,drop 标志藏在实现 Drop 的类型的隐藏字段里。

最后修改 August 11, 2026: 更新 (70a5af133)