From 62191f6b4e2636d2506764ae65a9084c5aa35294 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 19 Jan 2019 14:12:04 +0000 Subject: [PATCH 1/5] Document drop scopes --- src/destructors.md | 394 ++++++++++++++++++++++++++++++++++++++------- src/expressions.md | 72 +-------- 2 files changed, 346 insertions(+), 120 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index a24451d82..e989c6853 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -1,28 +1,24 @@ # 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] in Rust 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] or [tuple] are dropped in declaration order. + * The fields of the active [enum variant] are dropped in declaration 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. @@ -37,61 +33,347 @@ impl Drop for ShowOnDrop { } } +let mut overwritten = ShowOnDrop("drops when overwritten"); +overwritten = ShowOnDrop("drops when scope ends"); + +let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second")); + +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. +``` + +## 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 + * [Block expressions] the expression and block scopes 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 + it's for. +* The parent of the expression for a given `match` arm is that arm's scope. +* 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. The actual function parameter is +dropped after any named parameters that are bound to parts of it. + +```rust +# struct ShowOnDrop(&'static str); +# impl Drop for ShowOnDrop { +# 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, _): (ShowOnDrop, ShowOnDrop), + (_, y): (ShowOnDrop, ShowOnDrop), +) {} + +// drop order is 3 2 0 1 +patterns_in_parameters( + (ShowOnDrop("0"), ShowOnDrop("1")), + (ShowOnDrop("2"), ShowOnDrop("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 are associated to the arm scope of the `match` arm that they are declared +in. + +```rust +# struct ShowOnDrop(&'static str); +# impl Drop for ShowOnDrop { +# fn drop(&mut self) { +# println!("drop({})", self.0); +# } +# } +let declared_first = ShowOnDrop("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 = ShowOnDrop("Dropped in inner scope"); } -# println!(""); -{ - // Tuple elements drop in forwards order - let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second")); +let declared_last = ShowOnDrop("Dropped first in outer scope"); +``` + +If multiple patterns are used in the same arm for a match, 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 to a `static`. + +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]. + +> Note: 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. + +Some examples: + +```rust +# struct ShowOnDrop(&'static str); +# impl Drop for ShowOnDrop { +# fn drop(&mut self) { +# println!("drop({})", self.0); +# } +# } +let local_var = ShowOnDrop("local var"); + +// Dropped once the condition has been evaluated +if ShowOnDrop("If condition").0 == "If condition" { + // Dropped at the end of the block + ShowOnDrop("If body").0 +} else { + unreachable!() +}; + +// Dropped at the end of the statement +(ShowOnDrop("first operand").0 == "" +// Dropped at the ) +|| ShowOnDrop("second operand").0 == "") +// Dropped at the end of the expression +|| ShowOnDrop("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 ShowOnDrop("Matched value in final expression") { + // Dropped once the condition has been evaluated + _ if ShowOnDrop("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 ShowOnDrop(&'static str); +# impl Drop for ShowOnDrop { +# 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 + ( + ShowOnDrop("Outer tuple first"), + ShowOnDrop("Outer tuple second"), + ( + ShowOnDrop("Inner tuple first"), + ShowOnDrop("Inner tuple second"), + break, + ), + ShowOnDrop("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 + +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* if it is + +* The initializer expression. +* The operand of a 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][block expressions]. + +So the borrow expressions in `&mut 0`, `(&1, &mut 2)` and `Some { 0: &mut 3 }` +are all extending expressions, while the borrows in `&0 + &1` and +`Some(&mut 0)` are not. + +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()` lives 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 -[variable]: variables.md +[interior mutability]: interior-mutability.md +[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators +[statement]: statements.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 +[variable]: variables.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 expressions]: 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..3413adc07 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -159,69 +159,13 @@ 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](#constant-promotion) +to a `static`. The [drop scope] of the temporary is usually the end of the +enclosing statement. ### Implicit Borrows @@ -304,14 +248,15 @@ 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 @@ -321,7 +266,6 @@ They are never allowed before: [Temporary values]: #temporary-lifetimes [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 From 79fcc6e4453919977b8b3bdf5aee71146c89217d Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 20 Jan 2019 12:58:54 +0000 Subject: [PATCH 2/5] Fix links and sentence structure --- src/destructors.md | 6 ++++-- src/expressions.md | 10 +++++----- src/expressions/loop-expr.md | 2 +- src/expressions/operator-expr.md | 2 +- src/introduction.md | 6 +++--- src/types/pointer.md | 2 +- src/variables.md | 2 +- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index e989c6853..3e3889cfc 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -278,7 +278,8 @@ scope of the initializer expression is extended. #### Extending based on expressions -For a let statement with an initializer, an *extending expression* if it is +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 a extending [borrow expression]. @@ -342,8 +343,9 @@ variable or field from being dropped automatically. [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 [statement]: statements.md -[temporary]: expressions.md#temporary-lifetimes +[temporary]: expressions.md#temporaries [variable]: variables.md [array]: types/array.md diff --git a/src/expressions.md b/src/expressions.md index 3413adc07..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 @@ -163,9 +163,8 @@ The following expressions can be mutable place expression contexts: 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](#constant-promotion) -to a `static`. The [drop scope] of the temporary is usually the end of the -enclosing statement. +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 @@ -260,10 +259,11 @@ They are never allowed before: [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 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, From 0e735f9eb7bc97f69a8544abf399c1081771206c Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 28 Jan 2019 19:20:59 +0000 Subject: [PATCH 3/5] Apply suggestions from code review Style and grammar fixes Co-Authored-By: matthewjasper --- src/destructors.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 3e3889cfc..3d25c2903 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -1,12 +1,12 @@ # Destructors -When an [initialized] [variable] or [temporary] in Rust goes out of +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 `T` consists of +The destructor of a type `T` consists of: 1. If `T: Drop`, calling [`::drop`] 2. Recursively running the destructor of all of its fields. @@ -41,7 +41,7 @@ let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second")); let moved; // No destructor run on assignment. moved = ShowOnDrop("Drops when moved"); -// drops now, but is then uninitialized +// Drops now, but is then uninitialized. moved; // Uninitialized does not drop. @@ -60,7 +60,7 @@ 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 +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. @@ -87,9 +87,9 @@ from the inside outwards. * 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 - it's for. + the guard is for. * The parent of the expression for a given `match` arm is that arm's scope. -* The parent of the arm scope is the scope of the match expression that it +* 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. @@ -97,7 +97,7 @@ from the inside outwards. ### Scopes of function parameters All function parameters are in the scope of the entire function body, so are -dropped last when evaluating the function. The actual function parameter is +dropped last when evaluating the function. Actual function parameters are dropped after any named parameters that are bound to parts of it. ```rust @@ -124,7 +124,7 @@ patterns_in_parameters( 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 are associated to the arm scope of the `match` arm that they are declared +`match` expression are associated to the arm scope of the `match` arm that they are declared in. ```rust @@ -141,7 +141,7 @@ let declared_first = ShowOnDrop("Dropped last in outer scope"); let declared_last = ShowOnDrop("Dropped first in outer scope"); ``` -If multiple patterns are used in the same arm for a match, then an unspecified +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 @@ -256,7 +256,7 @@ let x = &mut 0; println!("{}", x); ``` -If a borrow, dereference, field or tuple indexing expression has an extended +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. @@ -267,7 +267,7 @@ 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 + 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`, @@ -282,13 +282,13 @@ 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 a extending [borrow 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], [braced struct][struct expression], or [tuple][tuple expression] expression. * The final expression of any extending [block expression][block expressions]. -So the borrow expressions in `&mut 0`, `(&1, &mut 2)` and `Some { 0: &mut 3 }` +So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some { 0: &mut 3 }` are all extending expressions, while the borrows in `&0 + &1` and `Some(&mut 0)` are not. @@ -320,7 +320,7 @@ Here are some examples where expressions don't have extended temporary scopes: # fn temp() {} # trait Use { fn use_temp(&self) -> &Self { self } } # impl Use for () {} -// The temporary that stores the result of `temp()` lives only lives until the +// 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 From 99da7c0fa84523dd7655e015d82a1125558057d8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 28 Jan 2019 19:56:41 +0000 Subject: [PATCH 4/5] Rename `ShowOnDrop` -> `PrintOnDrop` --- src/destructors.md | 75 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 3d25c2903..674765e7f 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -25,30 +25,30 @@ 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 = ShowOnDrop("drops when overwritten"); -overwritten = ShowOnDrop("drops when scope ends"); +let mut overwritten = PrintOnDrop("drops when overwritten"); +overwritten = PrintOnDrop("drops when scope ends"); -let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second")); +let tuple = (PrintOnDrop("Tuple first"), PrintOnDrop("Tuple second")); let moved; // No destructor run on assignment. -moved = ShowOnDrop("Drops when moved"); +moved = PrintOnDrop("Drops when moved"); // Drops now, but is then uninitialized. moved; // Uninitialized does not drop. -let uninitialized: ShowOnDrop; +let uninitialized: PrintOnDrop; // After a partial move, only the remaining fields are dropped. -let mut partial_move = (ShowOnDrop("first"), ShowOnDrop("forgotten")); +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. @@ -101,22 +101,22 @@ dropped last when evaluating the function. Actual function parameters are dropped after any named parameters that are bound to parts of it. ```rust -# struct ShowOnDrop(&'static str); -# impl Drop for ShowOnDrop { +# 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, _): (ShowOnDrop, ShowOnDrop), - (_, y): (ShowOnDrop, ShowOnDrop), + (x, _): (PrintOnDrop, PrintOnDrop), + (_, y): (PrintOnDrop, PrintOnDrop), ) {} // drop order is 3 2 0 1 patterns_in_parameters( - (ShowOnDrop("0"), ShowOnDrop("1")), - (ShowOnDrop("2"), ShowOnDrop("3")), + (PrintOnDrop("0"), PrintOnDrop("1")), + (PrintOnDrop("2"), PrintOnDrop("3")), ); ``` @@ -128,17 +128,17 @@ the block that contains the `let` statement. Local variables declared in a in. ```rust -# struct ShowOnDrop(&'static str); -# impl Drop for ShowOnDrop { +# struct PrintOnDrop(&'static str); +# impl Drop for PrintOnDrop { # fn drop(&mut self) { # println!("drop({})", self.0); # } # } -let declared_first = ShowOnDrop("Dropped last in outer scope"); +let declared_first = PrintOnDrop("Dropped last in outer scope"); { - let declared_in_block = ShowOnDrop("Dropped in inner scope"); + let declared_in_block = PrintOnDrop("Dropped in inner scope"); } -let declared_last = ShowOnDrop("Dropped first in outer scope"); +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 @@ -157,7 +157,8 @@ smallest scope that contains the expression and is for one of the following: * 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 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]. @@ -168,36 +169,36 @@ smallest scope that contains the expression and is for one of the following: Some examples: ```rust -# struct ShowOnDrop(&'static str); -# impl Drop for ShowOnDrop { +# struct PrintOnDrop(&'static str); +# impl Drop for PrintOnDrop { # fn drop(&mut self) { # println!("drop({})", self.0); # } # } -let local_var = ShowOnDrop("local var"); +let local_var = PrintOnDrop("local var"); // Dropped once the condition has been evaluated -if ShowOnDrop("If condition").0 == "If condition" { +if PrintOnDrop("If condition").0 == "If condition" { // Dropped at the end of the block - ShowOnDrop("If body").0 + PrintOnDrop("If body").0 } else { unreachable!() }; // Dropped at the end of the statement -(ShowOnDrop("first operand").0 == "" +(PrintOnDrop("first operand").0 == "" // Dropped at the ) -|| ShowOnDrop("second operand").0 == "") +|| PrintOnDrop("second operand").0 == "") // Dropped at the end of the expression -|| ShowOnDrop("third operand").0 == ""; +|| 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 ShowOnDrop("Matched value in final expression") { +match PrintOnDrop("Matched value in final expression") { // Dropped once the condition has been evaluated - _ if ShowOnDrop("guard condition").0 == "" => (), + _ if PrintOnDrop("guard condition").0 == "" => (), _ => (), } ``` @@ -211,8 +212,8 @@ 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 ShowOnDrop(&'static str); -# impl Drop for ShowOnDrop { +# struct PrintOnDrop(&'static str); +# impl Drop for PrintOnDrop { # fn drop(&mut self) { # println!("drop({})", self.0); # } @@ -220,14 +221,14 @@ operands to an expression breaks out of the expression, returns, or panics. loop { // Tuple expression doesn't finish evaluating so operands drop in reverse order ( - ShowOnDrop("Outer tuple first"), - ShowOnDrop("Outer tuple second"), + PrintOnDrop("Outer tuple first"), + PrintOnDrop("Outer tuple second"), ( - ShowOnDrop("Inner tuple first"), - ShowOnDrop("Inner tuple second"), + PrintOnDrop("Inner tuple first"), + PrintOnDrop("Inner tuple second"), break, ), - ShowOnDrop("Never created"), + PrintOnDrop("Never created"), ); } ``` From c5648e6303632d99ac96edbc7aee7c032dc28891 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 28 Jan 2019 21:36:38 +0000 Subject: [PATCH 5/5] Address review feedback --- src/destructors.md | 53 +++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 674765e7f..c70c7010d 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -10,8 +10,9 @@ The destructor of a type `T` consists of: 1. If `T: Drop`, calling [`::drop`] 2. Recursively running the destructor of all of its fields. - * The fields of a [struct] or [tuple] are dropped in declaration 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 @@ -71,7 +72,8 @@ Given a function, or closure, there are drop scopes for: * Each [statement] * Each [expression] * Each block, including the function body - * [Block expressions] the expression and block scopes are the same scope. + * 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 @@ -82,13 +84,14 @@ from the inside outwards. * 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 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 for a given `match` arm is that arm's scope. +* 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 @@ -97,8 +100,8 @@ from the inside outwards. ### Scopes of function parameters All function parameters are in the scope of the entire function body, so are -dropped last when evaluating the function. Actual function parameters are -dropped after any named parameters that are bound to parts of it. +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); @@ -124,8 +127,8 @@ patterns_in_parameters( 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. +`match` expression are associated to the arm scope of the `match` arm that they +are declared in. ```rust # struct PrintOnDrop(&'static str); @@ -141,14 +144,14 @@ let declared_first = PrintOnDrop("Dropped last in outer scope"); 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. +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 to a `static`. +[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: @@ -162,9 +165,16 @@ smallest scope that contains the expression and is for one of the following: * The expression for a match arm. * The second operand of a [lazy boolean expression]. -> Note: 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. +> **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: @@ -245,6 +255,9 @@ 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 @@ -287,11 +300,11 @@ expression which is one of the following: * 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][block expressions]. +* 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, while the borrows in `&0 + &1` and -`Some(&mut 0)` are not. +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. @@ -345,6 +358,8 @@ variable or field from being dropped automatically. [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 @@ -362,7 +377,7 @@ variable or field from being dropped automatically. [tuple struct pattern]: patterns.md#tuple-struct-patterns [array expression]: expressions/array-expr.md#array-expressions -[block expressions]: expressions/block-expr.md +[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