r[destructors.intro] When an initialized variable or temporary goes out of scope, its destructor is run, or it is dropped. Assignment also runs the destructor of its left-hand operand, if it's initialized. If a variable has been partially initialized, only its initialized fields are dropped.
r[destructors.operation] The destructor of a type T
consists of:
T: Drop
, calling <T as core::ops::Drop>::drop
r[destructors.drop_in_place] If a destructor must be run manually, such as when implementing your own smart pointer, [core::ptr::drop_in_place
] can be used.
Some examples:
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.
r[destructors.scope]
r[destructors.scope.intro] Each variable or temporary is associated to a drop scope. When control flow leaves a drop scope all variables associated to that scope are dropped in reverse order of declaration (for variables) or creation (for temporaries).
r[destructors.scope.desugaring] Drop scopes can be determined by replacing for
, if
, and while
expressions with equivalent expressions using match
, loop
and break
.
r[destructors.scope.operators] Overloaded operators are not distinguished from built-in operators and binding modes are not considered.
r[destructors.scope.list] Given a function, or closure, there are drop scopes for:
r[destructors.scope.function]
r[destructors.scope.statement]
r[destructors.scope.expression]
r[destructors.scope.block]
r[destructors.scope.match-arm]
match
expressionr[destructors.scope.nesting] Drop scopes are nested within one another as follows. When multiple scopes are left at once, such as when returning from a function, variables are dropped from the inside outwards.
r[destructors.scope.nesting.function]
r[destructors.scope.nesting.function-body]
r[destructors.scope.nesting.expr-statement]
r[destructors.scope.nesting.let-initializer]
let
statement is the let
statement's scope.r[destructors.scope.nesting.statement]
r[destructors.scope.nesting.match-guard]
match
guard is the scope of the arm that the guard is for.r[destructors.scope.nesting.match-arm]
=>
in a match
expression is the scope of the arm that it's in.r[destructors.scope.nesting.match]
match
expression that it belongs to.r[destructors.scope.nesting.other]
r[destructors.scope.params]
All function parameters are in the scope of the entire function body, so are dropped last when evaluating the function. Each actual function parameter is dropped after any bindings introduced in that parameter's pattern.
# 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")), );
r[destructors.scope.bindings]
r[destructors.scope.bindings.intro] Local variables declared in a let
statement are associated to the scope of the block that contains the let
statement. Local variables declared in a match
expression are associated to the arm scope of the match
arm that they are declared in.
# 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");
r[destructors.scope.bindings.patterns] Variables in patterns are dropped in reverse order of declaration within the pattern.
# 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"), );
r[destructors.scope.bindings.or-patterns] For the purpose of drop order, or-patterns declare bindings in the order given by the first subpattern.
# 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"), ]));
r[destructors.scope.temporary]
r[destructors.scope.temporary.intro] The temporary scope of an expression is the scope that is used for the temporary variable that holds the result of that expression when used in a place context, unless it is promoted.
r[destructors.scope.temporary.enclosing] Apart from lifetime extension, the temporary scope of an expression is the smallest scope that contains the expression and is one of the following:
if
, while
or loop
expression.else
block of an if
expression.if
or while
expression, or a match
guard.if
([destructors.scope.temporary.edition2024]).while
.[!NOTE] The scrutinee of a
match
expression is not a temporary scope, so temporaries in the scrutinee can be dropped after thematch
expression. For example, the temporary for1
inmatch 1 { ref mut z => z };
lives until the end of the statement.
[!NOTE] The desugaring of a destructuring assignment restricts the temporary scope of its assigned value operand (the RHS). For details, see [expr.assign.destructure.tmp-scopes].
r[destructors.scope.temporary.edition2024]
[!EDITION-2024] The 2024 edition added two new temporary scope narrowing rules:
if let
temporaries are dropped before theelse
block, and temporaries of tail expressions of blocks are dropped immediately after the tail expression is evaluated.
Some examples:
# #![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") { // Dropped once the condition has been evaluated _ if PrintOnDrop("guard condition").0 == "" => (), _ => (), }
r[destructors.scope.operands]
Temporaries are also created to hold the result of operands to an expression while the other operands are evaluated. The temporaries are associated to the scope of the expression with that operand. Since the temporaries are moved from once the expression is evaluated, dropping them has no effect unless one of the operands to an expression breaks out of the expression, returns, or panics.
# 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"), ); }
r[destructors.scope.const-promotion]
Promotion of a value expression to a 'static
slot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g. &None
always has the type &'static Option<_>
, as it contains nothing disallowed).
r[destructors.scope.lifetime-extension]
[!NOTE] The exact rules for temporary lifetime extension are subject to change. This is describing the current behavior only.
r[destructors.scope.lifetime-extension.let] The temporary scopes for expressions in let
statements are sometimes extended to the scope of the block containing the let
statement. This is done when the usual temporary scope would be too small, based on certain syntactic rules. For example:
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);
r[destructors.scope.lifetime-extension.static] Lifetime extension also applies to static
and const
items, where it makes temporaries live until the end of the program. For example:
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);
r[destructors.scope.lifetime-extension.sub-expressions] If a borrow, dereference, field, or tuple indexing expression has an extended temporary scope, then so does its operand. If an indexing expression has an extended temporary scope, then the indexed expression also has an extended temporary scope.
r[destructors.scope.lifetime-extension.patterns]
r[destructors.scope.lifetime-extension.patterns.extending] An extending pattern is either:
An identifier pattern that binds by reference or mutable reference.
# fn temp() {} let ref x = temp(); // Binds by reference. # x; let ref mut x = temp(); // Binds by mutable reference. # x;
A struct, tuple, tuple struct, slice, or or-pattern where at least one of the direct subpatterns is an extending pattern.
# 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));
So ref x
, V(ref x)
and [ref x, y]
are all extending patterns, but x
, &ref x
and &(ref x,)
are not.
r[destructors.scope.lifetime-extension.patterns.let] If the pattern in a let
statement is an extending pattern then the temporary scope of the initializer expression is extended.
# fn temp() {} // This is an extending pattern, so the temporary scope is extended. let ref x = *&temp(); // OK # x;
# 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;
# 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;
r[destructors.scope.lifetime-extension.exprs]
r[destructors.scope.lifetime-extension.exprs.extending] For a let statement with an initializer, an extending expression is an expression which is one of the following:
if
expression's consequent, else if
, or else
block.match
expression.[!NOTE] The desugaring of a destructuring assignment makes its assigned value operand (the RHS) an extending expression within a newly-introduced block. For details, see [expr.assign.destructure.tmp-ext].
So the borrow expressions in &mut 0
, (&1, &mut 2)
, and Some(&mut 3)
are all extending expressions. The borrows in &0 + &1
and f(&mut 0)
are not.
r[destructors.scope.lifetime-extension.exprs.borrows] The operand of an extending borrow expression has its temporary scope extended.
r[destructors.scope.lifetime-extension.exprs.super-macros] The super temporaries of an extending super macro call expression have their scopes extended.
[!NOTE]
rustc
does not treat array repeat operands of extending array expressions as extending expressions. Whether it should is an open question.For details, see Rust issue #146092.
Here are some examples where expressions have extended temporary scopes:
# 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; 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));
Here are some examples where expressions don't have extended temporary scopes:
# 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;
# 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;
# fn temp() {} // Scrutinees of match expressions are not extending expressions. let x = match &temp() { x => x }; // ERROR # x;
# fn temp() {} // Final expressions of `async` blocks are not extending expressions. let x = async { &temp() }; // ERROR # x;
# fn temp() {} // Final expressions of closures are not extending expressions. let x = || &temp(); // ERROR # x;
# fn temp() {} // Operands of loop breaks are not extending expressions. let x = loop { break &temp() }; // ERROR # x;
# fn temp() {} // Operands of breaks to labels are not extending expressions. let x = 'a: { break 'a &temp() }; // ERROR # x;
# 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
# fn temp() {} // As above. format_args!("{:?}", { &temp() }); // ERROR
r[destructors.forget]
r[destructors.manually-suppressing]
[core::mem::forget
] can be used to prevent the destructor of a variable from being run, and [core::mem::ManuallyDrop
] provides a wrapper to prevent a variable or field from being dropped automatically.
[!NOTE] Preventing a destructor from being run via [
core::mem::forget
] or other means is safe even if it has a type that isn't'static
. Besides the places where destructors are guaranteed to run as defined by this document, types may not safely rely on a destructor being run for soundness.
r[destructors.process-termination]
There are some ways to terminate the process without unwinding, in which case destructors will not be run.
The standard library provides [std::process::exit
] and [std::process::abort
] to do this explicitly. Additionally, if the [panic handler][panic.panic_handler.std] is set to abort
, panicking will always terminate the process without destructors being run.
There is one additional case to be aware of: when a panic reaches a non-unwinding ABI boundary, either no destructors will run, or all destructors up until the ABI boundary will run.