析构器
当已初始化的变量或临时值超出作用域时,其析构器(Destructor)将运行或被丢弃(Drop)。赋值也会运行其左操作数的析构器(如果已初始化)。如果变量已部分初始化,则只丢弃其已初始化的字段。
类型 T 的析构器包括:
- 如果
T: Drop,调用<T as core::ops::Drop>::drop - 递归运行其所有字段的析构器。
如果必须手动运行析构器,例如实现自己的智能指针时,可以使用 core::ptr::drop_in_place。
一些示例:
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("{}", self.0);
}
}
let mut overwritten = PrintOnDrop("drops when overwritten");
overwritten = PrintOnDrop("drops when scope ends");
let tuple = (PrintOnDrop("Tuple first"), PrintOnDrop("Tuple second"));
let moved;
// No destructor run on assignment.
moved = PrintOnDrop("Drops when moved");
// Drops now, but is then uninitialized.
moved;
// Uninitialized does not drop.
let uninitialized: PrintOnDrop;
// After a partial move, only the remaining fields are dropped.
let mut partial_move = (PrintOnDrop("first"), PrintOnDrop("forgotten"));
// Perform a partial move, leaving only `partial_move.0` initialized.
core::mem::forget(partial_move.1);
// When partial_move's scope ends, only the first field is dropped.
}
丢弃作用域
每个变量或临时值都与一个丢弃作用域相关联。当控制流离开丢弃作用域时,与该作用域关联的所有变量按声明顺序(对于变量)或创建顺序(对于临时值)的逆序被丢弃。
丢弃作用域可以通过将 for、if 和 while 表达式替换为使用 match、loop 和 break 的等效表达式来确定。
重载运算符与内置运算符没有区别,不考虑绑定模式。
给定一个函数或闭包,有以下丢弃作用域:
- 整个函数
- 每个语句
- 每个表达式
- 每个块,包括函数主体
- 对于块表达式,块和表达式的作用域是相同的作用域。
match表达式的每个分支
丢弃作用域嵌套如下。当同时离开多个作用域时(例如从函数返回时),变量从内向外丢弃。
- 整个函数作用域是最外层的作用域。
- 函数主体块包含在整个函数的作用域内。
- 表达式语句中表达式的父级是语句的作用域。
let语句初始化器的父级是let语句的作用域。
- 语句作用域的父级是包含该语句的块的作用域。
match守卫表达式的父级是该守卫所属分支的作用域。
match表达式中=>之后的表达式的父级是其所在分支的作用域。
- 分支作用域的父级是其所属的
match表达式的作用域。
- 所有其他作用域的父级是直接封闭表达式的作用域。
函数参数的作用域
所有函数参数都在整个函数主体的作用域内,因此在求值函数时最后丢弃。每个实际函数参数在该参数模式中引入的任何绑定之后丢弃。
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
// Drops `y`, then the second parameter, then `x`, then the first parameter
fn patterns_in_parameters(
(x, _): (PrintOnDrop, PrintOnDrop),
(_, y): (PrintOnDrop, PrintOnDrop),
) {}
// drop order is 3 2 0 1
patterns_in_parameters(
(PrintOnDrop("0"), PrintOnDrop("1")),
(PrintOnDrop("2"), PrintOnDrop("3")),
);
}
局部变量的作用域
在 let 语句中声明的局部变量与包含 let 语句的块的作用域相关联。
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
let declared_first = PrintOnDrop("Dropped last in outer scope");
{
let declared_in_block = PrintOnDrop("Dropped in inner scope");
}
let declared_last = PrintOnDrop("Dropped first in outer scope");
}
在 match 表达式或模式匹配 match 守卫中声明的局部变量与其声明所在的 match 分支的分支作用域相关联。
#![allow(unused)]
fn main() {
#![allow(irrefutable_let_patterns)]
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
match PrintOnDrop("Dropped last in the first arm's scope") {
// When guard evaluation succeeds, control-flow stays in the arm and
// values may be moved from the scrutinee into the arm's bindings,
// causing them to be dropped in the arm's scope.
x if let y = PrintOnDrop("Dropped second in the first arm's scope")
&& let z = PrintOnDrop("Dropped first in the first arm's scope") =>
{
let declared_in_block = PrintOnDrop("Dropped in inner scope");
// Pattern-matching guards' bindings and temporaries are dropped in
// reverse order, dropping each guard condition operand's bindings
// before its temporaries. Lastly, variables bound by the arm's
// pattern are dropped.
}
_ => unreachable!(),
}
match PrintOnDrop("Dropped in the enclosing temporary scope") {
// When guard evaluation fails, control-flow leaves the arm scope,
// causing bindings and temporaries from earlier pattern-matching
// guard condition operands to be dropped. This occurs before evaluating
// the next arm's guard or body.
_ if let y = PrintOnDrop("Dropped in the first arm's scope")
&& false => unreachable!(),
// When a guard is executed multiple times due to self-overlapping
// or-patterns, control-flow leaves the arm scope when the guard fails
// and re-enters the arm scope before executing the guard again.
_ | _ if let y = PrintOnDrop("Dropped in the second arm's scope twice")
&& false => unreachable!(),
_ => {},
}
}
模式中的变量按模式内声明的逆序丢弃。
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
let (declared_first, declared_last) = (
PrintOnDrop("Dropped last"),
PrintOnDrop("Dropped first"),
);
}
出于丢弃顺序的目的,或模式按第一个子模式给出的顺序声明绑定。
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
// Drops `x` before `y`.
fn or_pattern_drop_order<T>(
(Ok([x, y]) | Err([y, x])): Result<[T; 2], [T; 2]>
// ^^^^^^^^^^ ^^^^^^^^^^^ This is the second subpattern.
// |
// This is the first subpattern.
//
// In the first subpattern, `x` is declared before `y`. Since it is
// the first subpattern, that is the order used even if the second
// subpattern, where the bindings are declared in the opposite
// order, is matched.
) {}
// Here we match the first subpattern, and the drops happen according
// to the declaration order in the first subpattern.
or_pattern_drop_order(Ok([
PrintOnDrop("Declared first, dropped last"),
PrintOnDrop("Declared last, dropped first"),
]));
// Here we match the second subpattern, and the drops still happen
// according to the declaration order in the first subpattern.
or_pattern_drop_order(Err([
PrintOnDrop("Declared last, dropped first"),
PrintOnDrop("Declared first, dropped last"),
]));
}
临时作用域
表达式的临时作用域是用于保存该表达式在位置上下文中使用时结果的临时变量的作用域,除非它被提升。
除了生命周期扩展外,表达式的临时作用域是包含该表达式的最小作用域,是以下之一:
- 整个函数。
- 一个语句。
if、while或loop表达式的主体。if表达式的else块。if或while表达式的非模式匹配条件表达式,或非模式匹配match守卫条件操作数。- 模式匹配守卫(如果存在)和
match分支的主体表达式。 - 惰性布尔表达式的每个操作数。
if的模式匹配条件和结果主体(destructors.scope.temporary.edition2024)。while的模式匹配条件和循环主体。- 块的尾部表达式的整体(destructors.scope.temporary.edition2024)。
Note
match表达式的审查者不是临时作用域,因此审查者中的临时值可以在match表达式之后被丢弃。例如,match 1 { ref mut z => z };中1的临时值存活到语句结束。
Note
解构赋值的脱糖限制了其赋值值操作数(RHS)的临时作用域。有关详细信息,请参阅 expr.assign.destructure.tmp-scopes。
2024 Edition differences
2024 版本添加了两个新的临时作用域缩小规则:
if let临时值在else块之前丢弃,块的尾部表达式的临时值在尾部表达式求值后立即丢弃。
一些示例:
#![allow(unused)]
fn main() {
#![allow(irrefutable_let_patterns)]
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
let local_var = PrintOnDrop("local var");
// Dropped once the condition has been evaluated
if PrintOnDrop("If condition").0 == "If condition" {
// Dropped at the end of the block
PrintOnDrop("If body").0
} else {
unreachable!()
};
if let "if let scrutinee" = PrintOnDrop("if let scrutinee").0 {
PrintOnDrop("if let consequent").0
// `if let consequent` dropped here
}
// `if let scrutinee` is dropped here
else {
PrintOnDrop("if let else").0
// `if let else` dropped here
};
while let x = PrintOnDrop("while let scrutinee").0 {
PrintOnDrop("while let loop body").0;
break;
// `while let loop body` dropped here.
// `while let scrutinee` dropped here.
}
// Dropped before the first ||
(PrintOnDrop("first operand").0 == ""
// Dropped before the )
|| PrintOnDrop("second operand").0 == "")
// Dropped before the ;
|| PrintOnDrop("third operand").0 == "";
// Scrutinee is dropped at the end of the function, before local variables
// (because this is the tail expression of the function body block).
match PrintOnDrop("Matched value in final expression") {
// Non-pattern-matching guards' temporaries are dropped once the
// condition has been evaluated
_ if PrintOnDrop("guard condition").0 == "" => (),
// Pattern-matching guards' temporaries are dropped when leaving the
// arm's scope
_ if let "guard scrutinee" = PrintOnDrop("guard scrutinee").0 => {
let _ = &PrintOnDrop("lifetime-extended temporary in inner scope");
// `lifetime-extended temporary in inner scope` is dropped here
}
// `guard scrutinee` is dropped here
_ => (),
}
}
操作数
还会创建临时值来保存表达式操作数的结果,同时求值其他操作数。临时值与具有该操作数的表达式的作用域相关联。由于一旦表达式求值完成就会从临时值移出,除非表达式的操作数之一跳出表达式、返回或 panics,否则丢弃它们没有效果。
#![allow(unused)]
fn main() {
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("drop({})", self.0);
}
}
loop {
// Tuple expression doesn't finish evaluating so operands drop in reverse order
(
PrintOnDrop("Outer tuple first"),
PrintOnDrop("Outer tuple second"),
(
PrintOnDrop("Inner tuple first"),
PrintOnDrop("Inner tuple second"),
break,
),
PrintOnDrop("Never created"),
);
}
}
常量提升
当表达式可以在常量中编写并借用,并且该借用可以在表达式最初编写的位置被解引用而不改变运行时行为时,值表达式提升到 'static 槽位。也就是说,提升的表达式可以在编译时求值,结果值不包含内部可变性或析构器(这些属性尽可能基于值确定,例如 &None 始终具有类型 &'static Option<_>,因为它不包含任何不允许的内容)。
临时生命周期扩展
Note
临时生命周期扩展的确切规则可能会更改。这仅描述当前行为。
let 语句中表达式的临时作用域有时会扩展到包含 let 语句的块的作用域。当通常的临时作用域太小时,基于某些语法规则执行此操作。例如:
#![allow(unused)]
fn main() {
let x = &mut 0;
// Usually a temporary would be dropped by now, but the temporary for `0` lives
// to the end of the block.
println!("{}", x);
}
生命周期扩展也适用于 static 和 const 项,其中它使临时值存活到程序结束。例如:
#![allow(unused)]
fn main() {
const C: &Vec<i32> = &Vec::new();
// Usually this would be a dangling reference as the `Vec` would only
// exist inside the initializer expression of `C`, but instead the
// borrow gets lifetime-extended so it effectively has `'static` lifetime.
println!("{:?}", C);
}
如果借用、解引用、字段或元组索引表达式具有扩展的临时作用域,则其操作数也是如此。如果索引表达式具有扩展的临时作用域,则被索引的表达式也具有扩展的临时作用域。
基于模式的扩展
扩展模式是以下之一:
-
通过引用或可变引用绑定的标识符模式。
#![allow(unused)] fn main() { fn temp() {} let ref x = temp(); // Binds by reference. x; let ref mut x = temp(); // Binds by mutable reference. x; } -
结构体、元组、元组结构体、切片或或模式,其中至少一个直接子模式是扩展模式。
#![allow(unused)] fn main() { use core::sync::atomic::{AtomicU64, Ordering::Relaxed}; static X: AtomicU64 = AtomicU64::new(0); struct W<T>(T); impl<T> Drop for W<T> { fn drop(&mut self) { X.fetch_add(1, Relaxed); } } let W { 0: ref x } = W(()); // Struct pattern. x; let W(ref x) = W(()); // Tuple struct pattern. x; let (W(ref x),) = (W(()),); // Tuple pattern. x; let [W(ref x), ..] = [W(())]; // Slice pattern. x; let (Ok(W(ref x)) | Err(&ref x)) = Ok(W(())); // Or pattern. x; // // All of the temporaries above are still live here. assert_eq!(0, X.load(Relaxed)); }
所以 ref x、V(ref x) 和 [ref x, y] 都是扩展模式,但 x、&ref x 和 &(ref x,) 不是。
如果 let 语句中的模式是扩展模式,则初始化器表达式的临时作用域被扩展。
#![allow(unused)]
fn main() {
fn temp() {}
// This is an extending pattern, so the temporary scope is extended.
let ref x = *&temp(); // OK
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// This is neither an extending pattern nor an extending expression,
// so the temporary is dropped at the semicolon.
let &ref x = *&&temp(); // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// This is not an extending pattern but it is an extending expression,
// so the temporary lives beyond the `let` statement.
let &ref x = &*&temp(); // OK
x;
}
基于表达式的扩展
对于具有初始化器的 let 语句,扩展表达式是以下之一的表达式:
- 初始化器表达式。
- 扩展借用表达式的操作数。
- 扩展超级宏调用表达式的超级操作数。
- 扩展数组、强制转换、大括号结构体或元组表达式的操作数。
- 扩展元组结构体或元组枚举变体构造函数表达式的参数。
- 扩展块表达式的最终表达式,异步块表达式除外。
- 扩展
if表达式的结果、else if或else块的最终表达式。 - 扩展
match表达式的分支表达式。
Note
解构赋值的脱糖使其赋值值操作数(RHS)成为新引入的块中的扩展表达式。有关详细信息,请参阅 expr.assign.destructure.tmp-ext。
所以 &mut 0、(&1, &mut 2) 和 Some(&mut 3) 中的借用表达式都是扩展表达式。&0 + &1 和 f(&mut 0) 中的借用不是。
Note
rustc不将扩展数组表达式的数组重复操作数视为扩展表达式。是否应该这样做是一个开放问题。有关详细信息,请参阅 Rust issue #146092。
示例
以下是一些表达式具有扩展临时作用域的示例:
#![allow(unused)]
fn main() {
use core::pin::pin;
use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
static X: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)] struct S;
impl Drop for S { fn drop(&mut self) { X.fetch_add(1, Relaxed); } }
const fn temp() -> S { S }
let x = &temp(); // Operand of borrow.
x;
let x = &raw const *&temp(); // Operand of raw borrow.
assert_eq!(X.load(Relaxed), 0);
let x = &temp() as &dyn Send; // Operand of cast.
x;
let x = (&*&temp(),); // Operand of tuple constructor.
x;
struct W<T>(T);
let x = W(&temp()); // Argument to tuple struct constructor.
x;
let x = Some(&temp()); // Argument to tuple enum variant constructor.
x;
let x = { [Some(&temp())] }; // Final expr of block.
x;
let x = const { &temp() }; // Final expr of `const` block.
x;
let x = unsafe { &temp() }; // Final expr of `unsafe` block.
x;
let x = if true { &temp() } else { &temp() };
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Final exprs of `if`/`else` blocks.
x;
let x = match () { _ => &temp() }; // `match` arm expression.
x;
let x = pin!(temp()); // Super operand of super macro call expression.
x;
let x = pin!({ &mut temp() }); // As above.
x;
let x = format_args!("{:?}", temp()); // As above.
x;
//
// All of the temporaries above are still live here.
assert_eq!(0, X.load(Relaxed));
}
以下是一些表达式没有扩展临时作用域的示例:
#![allow(unused)]
fn main() {
fn temp() {}
// Arguments to function calls are not extending expressions. The
// temporary is dropped at the semicolon.
let x = core::convert::identity(&temp()); // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
trait Use { fn use_temp(&self) -> &Self { self } }
impl Use for () {}
// Receivers of method calls are not extending expressions.
let x = (&temp()).use_temp(); // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// Scrutinees of match expressions are not extending expressions.
let x = match &temp() { x => x }; // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// Final expressions of `async` blocks are not extending expressions.
let x = async { &temp() }; // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// Final expressions of closures are not extending expressions.
let x = || &temp(); // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// Operands of loop breaks are not extending expressions.
let x = loop { break &temp() }; // ERROR
x;
}
#![allow(unused)]
fn main() {
fn temp() {}
// Operands of breaks to labels are not extending expressions.
let x = 'a: { break 'a &temp() }; // ERROR
x;
}
#![allow(unused)]
fn main() {
use core::pin::pin;
fn temp() {}
// The argument to `pin!` is only an extending expression if the call
// is an extending expression. Since it's not, the inner block is not
// an extending expression, so the temporaries in its trailing
// expression are dropped immediately.
pin!({ &temp() }); // ERROR
}
#![allow(unused)]
fn main() {
fn temp() {}
// As above.
format_args!("{:?}", { &temp() }); // ERROR
}
不运行析构器
手动抑制析构器
core::mem::forget 可用于防止变量的析构器被运行,core::mem::ManuallyDrop 提供了一个包装器来防止变量或字段被自动丢弃。
Note
通过
core::mem::forget或其他方式防止析构器运行是安全的,即使其类型不是'static。除了本文档定义的保证运行析构器的位置外,类型可能不会安全地依赖析构器运行来保证健全性。
不展开的进程终止
有几种方法可以在不展开的情况下终止进程,在这种情况下不会运行析构器。
标准库提供 std::process::exit 和 std::process::abort 来显式执行此操作。此外,如果panic 处理器设置为 abort,panicking 将始终终止进程而不运行析构器。
还有一个需要注意的额外情况:当 panic 到达非展开 ABI 边界时,要么不运行析构器,要么运行直到 ABI 边界的所有析构器。