diff --git a/src/destructors.md b/src/destructors.md index a24451d82..c70c7010d 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -1,97 +1,397 @@ # Destructors -When an [initialized] [variable] in Rust goes out of scope or a [temporary] -is no longer needed its _destructor_ is run. [Assignment] also runs the -destructor of its left-hand operand, unless it's an uninitialized variable. If a -[struct] variable has been partially initialized, only its initialized fields -are dropped. +When an [initialized] [variable] or [temporary] goes out of +[scope](#drop-scopes) 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. -The destructor of a type consists of +The destructor of a type `T` consists of: -1. Calling its [`std::ops::Drop::drop`] method, if it has one. +1. If `T: Drop`, calling [`::drop`] 2. Recursively running the destructor of all of its fields. - * The fields of a [struct], [tuple] or [enum variant] are dropped in - declaration order. \* - * The elements of an [array] or owned [slice][array] are dropped from the - first element to the last. \* - * The captured values of a [closure] are dropped in an unspecified order. + * The fields of a [struct] are dropped in declaration order. + * The fields of the active [enum variant] are dropped in declaration order. + * The fields of a [tuple] are dropped in order. + * The elements of an [array] or owned [slice] are dropped from the + first element to the last. + * The variables that a [closure] captures by move are dropped in an + unspecified order. * [Trait objects] run the destructor of the underlying type. * Other types don't result in any further drops. -\* This order was stabilized in [RFC 1857]. - -Variables are dropped in reverse order of declaration. Variables declared in -the same pattern drop in an unspecified ordered. - If a destructor must be run manually, such as when implementing your own smart pointer, [`std::ptr::drop_in_place`] can be used. Some examples: ```rust -struct ShowOnDrop(&'static str); +struct PrintOnDrop(&'static str); -impl Drop for ShowOnDrop { +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. +``` + +## Drop scopes + +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). + +Drop scopes are determined after replacing [`for`], [`if let`], and +[`while let`] expressions with the equivalent expressions using [`match`]. +Overloaded operators are not distinguished from built-in operators and [binding +modes] are not considered. + +Given a function, or closure, there are drop scopes for: + +* The entire function +* Each [statement] +* Each [expression] +* Each block, including the function body + * In the case of a [block expression], the scope for the block and the + expression are the same scope. +* Each arm of a `match` expression + +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. + +* The entire function scope is the outer most scope. +* The function body block is contained within the scope of the entire function. +* The parent of the expression in an expression statement is the scope of the + statement. +* The parent of the initializer of a [`let` statement] is the `let` statement's + scope. +* The parent of a statement scope is the scope of the block that contains the + statement. +* The parent of the expression for a `match` guard is the scope of the arm that + the guard is for. +* The parent of the expression after the `=>` in a `match` expression is the + scope of the arm that it's in. +* The parent of the arm scope is the scope of the `match` expression that it + belongs to. +* The parent of all other scopes is the scope of the immediately enclosing + expression. + +### Scopes of function parameters + +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. + +```rust +# struct PrintOnDrop(&'static str); +# impl Drop for PrintOnDrop { +# fn drop(&mut self) { +# println!("drop({})", self.0); +# } +# } +// Drops the second parameter, then `y`, then the first parameter, then `x` +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")), +); +``` + +### Scopes of local variables + +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. + +```rust +# 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 mut overwritten = ShowOnDrop("Drops when overwritten"); - overwritten = ShowOnDrop("drops when scope ends"); -} -# println!(""); -{ - let declared_first = ShowOnDrop("Dropped last"); - let declared_last = ShowOnDrop("Dropped first"); + let declared_in_block = PrintOnDrop("Dropped in inner scope"); } -# println!(""); -{ - // Tuple elements drop in forwards order - let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second")); +let declared_last = PrintOnDrop("Dropped first in outer scope"); +``` + +If multiple patterns are used in the same arm for a `match` expression, then an +unspecified pattern will be used to determine the drop order. + +### Temporary scopes + +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]. + +Apart from lifetime extension, the temporary scope of an expression is the +smallest scope that contains the expression and is for one of the following: + +* The entire function body. +* A statement. +* The body of a [`if`], [`while`] or [`loop`] expression. +* The `else` block of an `if` expression. +* The condition expression of an `if` or `while` expression, or a `match` + guard. +* The expression for a match arm. +* The second operand of a [lazy boolean expression]. + +> **Notes**: +> +> Temporaries that are created in the final expression of a function +> body are dropped *after* any named variables bound in the function body, as +> there is no smaller enclosing temporary scope. +> +> The [scrutinee] of a `match` expression is not a temporary scope, so +> temporaries in the scrutinee can be dropped after the `match` expression. For +> example, the temporary for `1` in `match 1 { ref mut z => z };` lives until +> the end of the statement. + +Some examples: + +```rust +# 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!() +}; + +// Dropped at the end of the statement +(PrintOnDrop("first operand").0 == "" +// Dropped at the ) +|| PrintOnDrop("second operand").0 == "") +// Dropped at the end of the expression +|| PrintOnDrop("third operand").0 == ""; + +// Dropped at the end of the function, after local variables. +// Changing this to a statement containing a return expression would make the +// temporary be dropped before the local variables. Binding to a variable +// which is then returned would also make the temporary be dropped first. +match PrintOnDrop("Matched value in final expression") { + // Dropped once the condition has been evaluated + _ if PrintOnDrop("guard condition").0 == "" => (), + _ => (), } -# println!(""); +``` + +### 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. + +```rust +# struct PrintOnDrop(&'static str); +# impl Drop for PrintOnDrop { +# fn drop(&mut self) { +# println!("drop({})", self.0); +# } +# } loop { - // Tuple expression doesn't finish evaluating so temporaries drop in reverse order: - let partial_tuple = (ShowOnDrop("Temp first"), ShowOnDrop("Temp second"), break); -} -# println!(""); -{ - let moved; - // No destructor run on assignment. - moved = ShowOnDrop("Drops when moved"); - // drops now, but is then uninitialized - moved; - - // Uninitialized does not drop. - let uninitialized: ShowOnDrop; - - // After a partial move, only the remaining fields are dropped. - let mut partial_move = (ShowOnDrop("first"), ShowOnDrop("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. + // 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"), + ); } ``` +### Constant promotion + +Promotion of a value expression to a `'static` slot occurs when the expression +could be written in a constant, borrowed, and dereferencing that borrow 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). + +### Temporary lifetime extension + +> **Note**: The exact rules for temporary lifetime extension are subject to +> change. This is describing the current behavior only. + +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: + +```rust +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); +``` + +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. + +#### Extending based on patterns + +An *extending pattern* is either + +* An [identifier pattern] that binds by reference or mutable reference. +* A [struct][struct pattern], [tuple][tuple pattern], [tuple struct][tuple + struct pattern], or [slice][slice pattern] pattern where at least one of the + direct subpatterns is a extending pattern. + +So `ref x`, `V(ref x)` and `[ref x, y]` are all extending patterns, but `x`, +`&ref x` and `&(ref x,)` are not. + +If the pattern in a `let` statement is an extending pattern then the temporary +scope of the initializer expression is extended. + +#### Extending based on expressions + +For a let statement with an initializer, an *extending expression* is an +expression which is one of the following: + +* The initializer expression. +* The operand of an extending [borrow expression]. +* The operand(s) of an extending [array][array expression], [cast][cast + expression], [braced struct][struct expression], or [tuple][tuple expression] + expression. +* The final expression of any extending [block expression]. + +So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some { 0: &mut 3 }` +are all extending expressions. The borrows in `&0 + &1` and `Some(&mut 0)` are +not: the latter is syntactically a function call expression. + +The operand of any extending borrow expression has its temporary scope +extended. + +#### Examples + +Here are some examples where expressions have extended temporary scopes: + +```rust +# fn temp() {} +# trait Use { fn use_temp(&self) -> &Self { self } } +# impl Use for () {} +// The temporary that stores the result of `temp()` lives in the same scope +// as x in these cases. +let x = &temp(); +let x = &temp() as &dyn Send; +let x = (&*&temp(),); +let x = { [Some { 0: &temp(), }] }; +let ref x = temp(); +let ref x = *&temp(); +# x; +``` + +Here are some examples where expressions don't have extended temporary scopes: + +```rust,compile_fail +# fn temp() {} +# trait Use { fn use_temp(&self) -> &Self { self } } +# impl Use for () {} +// The temporary that stores the result of `temp()` only lives until the +// end of the let statement in these cases. + +let x = Some(&temp()); // ERROR +let x = (&temp()).use_temp(); // ERROR +# x; +``` + ## Not running destructors Not running destructors in Rust is safe even if it has a type that isn't `'static`. [`std::mem::ManuallyDrop`] provides a wrapper to prevent a variable or field from being dropped automatically. +[Assignment]: expressions/operator-expr.md#assignment-expressions +[binding modes]: patterns.md#binding-modes +[closure]: types/closure.md +[destructors]: destructors.md +[expression]: expressions.md +[identifier pattern]: patterns.md#identifier-patterns [initialized]: glossary.md#initialized +[interior mutability]: interior-mutability.md +[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators +[place context]: expressions.md#place-expressions-and-value-expressions +[promoted]: destructors.md#constant-promotion +[scrutinee]: glossary.md#scrutinee +[statement]: statements.md +[temporary]: expressions.md#temporaries [variable]: variables.md -[temporary]: expressions.md#temporary-lifetimes -[Assignment]: expressions/operator-expr.md#assignment-expressions -[`std::ops::Drop::drop`]: ../std/ops/trait.Drop.html -[RFC 1857]: https://github.com/rust-lang/rfcs/blob/master/text/1857-stabilize-drop-order.md -[struct]: types/struct.md -[tuple]: types/tuple.md -[enum variant]: types/enum.md + [array]: types/array.md -[closure]: types/closure.md +[enum variant]: types/enum.md +[slice]: types/slice.md +[struct]: types/struct.md [Trait objects]: types/trait-object.md +[tuple]: types/tuple.md + +[slice pattern]: patterns.md#slice-patterns +[struct pattern]: patterns.md#struct-patterns +[tuple pattern]: patterns.md#tuple-patterns +[tuple struct pattern]: patterns.md#tuple-struct-patterns + +[array expression]: expressions/array-expr.md#array-expressions +[block expression]: expressions/block-expr.md +[borrow expression]: expressions/operator-expr.md#borrow-operators +[cast expression]: expressions/operator-expr.md#type-cast-expressions +[struct expression]: expressions/struct-expr.md +[tuple expression]: expressions/tuple-expr.md#tuple-expressions + +[`for`]: expressions/loop-expr.md#iterator-loops +[`if let`]: expressions/if-expr.md#if-let-expressions +[`if`]: expressions/if-expr.md#if-expressions +[`let` statement]: statements.md#let-statements +[`loop`]: expressions/loop-expr.md#infinite-loops +[`match`]: expressions/match-expr.md +[`while let`]: expressions/loop-expr.md#predicate-pattern-loops +[`while`]: expressions/loop-expr.md#predicate-loops + +[`::drop`]: ../std/ops/trait.Drop.html#tymethod.drop [`std::ptr::drop_in_place`]: ../std/ptr/fn.drop_in_place.html [`std::mem::ManuallyDrop`]: ../std/mem/struct.ManuallyDrop.html diff --git a/src/expressions.md b/src/expressions.md index 68187f90b..122511b44 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -125,7 +125,7 @@ the remaining situations if that type is [`Sized`], then it may be possible to move the value. Only the following place expressions may be moved out of: * [Variables] which are not currently borrowed. -* [Temporary values](#temporary-lifetimes). +* [Temporary values](#temporaries). * [Fields][field] of a place expression which can be moved out of and doesn't implement [`Drop`]. * The result of [dereferencing][deref] an expression with type [`Box`] and @@ -159,69 +159,12 @@ The following expressions can be mutable place expression contexts: then evaluates the value being indexed, but not the index, in mutable place expression context. -### Temporary lifetimes +### Temporaries When using a value expression in most place expression contexts, a temporary unnamed memory location is created initialized to that value and the expression -evaluates to that location instead, except if promoted to `'static`. Promotion -of a value expression to a `'static` slot occurs when the expression could be -written in a constant, borrowed, and dereferencing that borrow 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). Otherwise, the -lifetime of temporary values is typically - -- the innermost enclosing statement; the tail expression of a block is - considered part of the statement that encloses the block, or -- the condition expression or the loop conditional expression if the - temporary is created in the condition expression of an `if` or in the loop - conditional expression of a `while` expression. - -When a temporary value expression is being created that is assigned into a -[`let` declaration][let], however, the temporary is created with the lifetime of -the enclosing block instead, as using the enclosing [`let` declaration][let] -would be a guaranteed error (since a pointer to the temporary -would be stored into a variable, but the temporary would be freed before the -variable could be used). The compiler uses simple syntactic rules to decide -which values are being assigned into a `let` binding, and therefore deserve a -longer temporary lifetime. - -Here are some examples: - -- `let x = foo(&temp())`. The expression `temp()` is a value expression. As it - is being borrowed, a temporary is created which will be freed after - the innermost enclosing statement; in this case, the `let` declaration. -- `let x = temp().foo()`. This is the same as the previous example, - except that the value of `temp()` is being borrowed via autoref on a - method-call. Here we are assuming that `foo()` is an `&self` method - defined in some trait, say `Foo`. In other words, the expression - `temp().foo()` is equivalent to `Foo::foo(&temp())`. -- `let x = if foo(&temp()) {bar()} else {baz()};`. The expression `temp()` is - a value expression. As the temporary is created in the condition expression - of an `if`, it will be freed at the end of the condition expression; - in this example before the call to `bar` or `baz` is made. -- `let x = if temp().must_run_bar {bar()} else {baz()};`. - Here we assume the type of `temp()` is a struct with a boolean field - `must_run_bar`. As the previous example, the temporary corresponding to - `temp()` will be freed at the end of the condition expression. -- `while foo(&temp()) {bar();}`. The temporary containing the return value from - the call to `temp()` is created in the loop conditional expression. Hence it - will be freed at the end of the loop conditional expression; in this example - before the call to `bar` if the loop body is executed. -- `let x = &temp()`. Here, the same temporary is being assigned into - `x`, rather than being passed as a parameter, and hence the - temporary's lifetime is considered to be the enclosing block. -- `let x = SomeStruct { foo: &temp() }`. As in the previous case, the - temporary is assigned into a struct which is then assigned into a - binding, and hence it is given the lifetime of the enclosing block. -- `let x = [ &temp() ]`. As in the previous case, the - temporary is assigned into an array which is then assigned into a - binding, and hence it is given the lifetime of the enclosing block. -- `let ref x = temp()`. In this case, the temporary is created using a ref - binding, but the result is the same: the lifetime is extended to the enclosing - block. +evaluates to that location instead, except if [promoted] to a `static`. The +[drop scope] of the temporary is usually the end of the enclosing statement. ### Implicit Borrows @@ -304,24 +247,25 @@ They are never allowed before: [deref]: expressions/operator-expr.md#the-dereference-operator [destructors]: destructors.md -[interior mutability]: interior-mutability.md -[`Box`]: ../std/boxed/struct.Box.md +[drop scope]: destructors.md#drop-scopes + +[`Box`]: ../std/boxed/struct.Box.html [`Copy`]: special-types-and-traits.md#copy [`Drop`]: special-types-and-traits.md#drop [`Sized`]: special-types-and-traits.md#sized [implicit borrow]: #implicit-borrows [implicitly mutably borrowed]: #implicit-borrows -[let]: statements.md#let-statements +[interior mutability]: interior-mutability.md [let statement]: statements.md#let-statements [Mutable `static` items]: items/static-items.md#mutable-statics [scrutinee]: glossary.md#scrutinee +[promoted]: destructors.md#constant-promotion [slice]: types/slice.md [statement]: statements.md [static variables]: items/static-items.md -[Temporary values]: #temporary-lifetimes +[Temporary values]: #temporaries [Variables]: variables.md - [_ArithmeticOrLogicalExpression_]: expressions/operator-expr.md#arithmetic-and-logical-binary-operators [_ArrayExpression_]: expressions/array-expr.md [_AsyncBlockExpression_]: expressions/block-expr.md#async-blocks diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 3bff9adaf..753465657 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -295,6 +295,6 @@ expression `()`. [_Pattern_]: ../patterns.md [`match` expression]: match-expr.md [scrutinee]: ../glossary.md#scrutinee -[temporary values]: ../expressions.md#temporary-lifetimes +[temporary values]: ../expressions.md#temporaries [_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators [`if let` expressions]: if-expr.md#if-let-expressions diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index b0f8c017e..a01902f25 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -455,7 +455,7 @@ assert_eq!(x, 14); [place expression]: ../expressions.md#place-expressions-and-value-expressions [value expression]: ../expressions.md#place-expressions-and-value-expressions -[temporary value]: ../expressions.md#temporary-lifetimes +[temporary value]: ../expressions.md#temporaries [float-int]: https://github.com/rust-lang/rust/issues/10184 [float-float]: https://github.com/rust-lang/rust/issues/15536 [`unit` type]: ../types/tuple.md diff --git a/src/introduction.md b/src/introduction.md index 2ef5413d5..b811dbeb1 100644 --- a/src/introduction.md +++ b/src/introduction.md @@ -139,9 +139,9 @@ attention to making those sections the best that they can be. [_Expression_]: expressions.md [cargo book]: ../cargo/index.html [cargo reference]: ../cargo/reference/index.html -[expressions chapter]: expressions.md -[lifetime of temporaries]: expressions.md#temporary-lifetimes -[linkage]: linkage.md +[expressions chapter]: expressions.html +[lifetime of temporaries]: expressions.html#temporaries +[linkage]: linkage.html [rustc book]: ../rustc/index.html [Notation]: notation.md [Discord]: https://discord.gg/rust-lang diff --git a/src/types/pointer.md b/src/types/pointer.md index de97815d2..7cbb1f173 100644 --- a/src/types/pointer.md +++ b/src/types/pointer.md @@ -56,4 +56,4 @@ and raw pointers. [_TypeNoBounds_]: ../types.md#type-expressions [`unsafe` operation]: ../unsafety.md [dynamically sized types]: ../dynamically-sized-types.md -[temporary value]: ../expressions.md#temporary-lifetimes +[temporary value]: ../expressions.md#temporaries diff --git a/src/variables.md b/src/variables.md index 9d586fbe2..5e5ec1bc1 100644 --- a/src/variables.md +++ b/src/variables.md @@ -1,7 +1,7 @@ # Variables A _variable_ is a component of a stack frame, either a named function parameter, -an anonymous [temporary](expressions.md#temporary-lifetimes), or a named local +an anonymous [temporary](expressions.md#temporaries), or a named local variable. A _local variable_ (or *stack-local* allocation) holds a value directly,