From cd7f8a83c556a0a40d0ef8b8e0c7b13949030b4b Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Thu, 29 Aug 2024 00:01:00 +0800 Subject: [PATCH 1/5] Add a section dedicated to Edition 2024 changes to temporary scopes --- src/destructors.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/destructors.md b/src/destructors.md index 9870b0148..88b640958 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -211,6 +211,8 @@ smallest scope that contains the expression and is one of the following: guard. * The body expression for a match arm. * Each operand of a [lazy boolean expression]. +* *Starting from Edition 2024* the pattern-matching condition and consequent body of [`if let`]. +* *Starting from Edition 2024* the entirety of the tail expression of a block. > **Notes**: > @@ -260,6 +262,97 @@ match PrintOnDrop("Matched value in final expression") { } ``` +#### Changes to temporary scopes by Edition 2024 + +Edition 2024 introduces two changes to the language regarding temporary scopes. +First, temporary values generated from evaluating the pattern-matching condition +of `if let` will be dropped earlier in general. + +```rust,edition2024 +struct Droppy(u8); +impl Droppy { + fn get(&self) -> Option<&u8> { + Some(&self.0) + } +} +impl Drop for Droppy { + fn drop(&mut self) { + println!("drop({})", self.0); + } +} + +fn call(x: &u8, y: u8, z: &u8) { + println!("call with {x} {y} {z}"); +} + +let x1 = 0; +let x2 = 3; + +call( + if let Some(x) = Droppy(0).get() { + println!("if let consequence"); + &x1 + } else { + &x2 + }, + { + let y = Droppy(1); + *y.get().unwrap() + }, + Droppy(2).get().unwrap() +); +println!("call ended"); +``` + +This program will print the following. + +```text +if let consequence +drop(0) +drop(1) +call with 0 1 2 +drop(2) +call ended +``` + +In other words, `Droppy(0)` is dropped before `Droppy(1)` because its temporary scope is +limited by Edition 2024 to the proper end of the `if let` expression. + +Second, temporary values generated from evaluating the tail expression of a block +or a function body will be dropped earlier than the local variable bindings. +The following example demonstrates the Edition 2024 order of events that values +and local variables are dropped. + +```rust,edition2024 +struct Droppy(u8); +impl Droppy { + fn get(&self) -> Option<&u8> { + Some(&self.0) + } +} +impl Drop for Droppy { + fn drop(&mut self) { + println!("drop({})", self.0); + } +} + +fn call() -> u8 { + let x = Droppy(0); + *x.get() + *Droppy(1).get() +} + +call(); +``` + +The print-out from this program will be the following. + +```text +drop(1) +drop(0) +``` + +In other words, `Droppy(1)` is dropped earlier than `x`. + ### Operands r[destructors.scope.operands] From ebdace99b68dd0e6f06eff1caac801a6ba999712 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 22 Nov 2024 10:15:39 -0800 Subject: [PATCH 2/5] Drop the 2024 temporary scope discussion The reference isn't intended to have guide-like material. Most of this should be covered in the edition guide. --- src/destructors.md | 91 ---------------------------------------------- 1 file changed, 91 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 88b640958..0bfa5f38b 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -262,97 +262,6 @@ match PrintOnDrop("Matched value in final expression") { } ``` -#### Changes to temporary scopes by Edition 2024 - -Edition 2024 introduces two changes to the language regarding temporary scopes. -First, temporary values generated from evaluating the pattern-matching condition -of `if let` will be dropped earlier in general. - -```rust,edition2024 -struct Droppy(u8); -impl Droppy { - fn get(&self) -> Option<&u8> { - Some(&self.0) - } -} -impl Drop for Droppy { - fn drop(&mut self) { - println!("drop({})", self.0); - } -} - -fn call(x: &u8, y: u8, z: &u8) { - println!("call with {x} {y} {z}"); -} - -let x1 = 0; -let x2 = 3; - -call( - if let Some(x) = Droppy(0).get() { - println!("if let consequence"); - &x1 - } else { - &x2 - }, - { - let y = Droppy(1); - *y.get().unwrap() - }, - Droppy(2).get().unwrap() -); -println!("call ended"); -``` - -This program will print the following. - -```text -if let consequence -drop(0) -drop(1) -call with 0 1 2 -drop(2) -call ended -``` - -In other words, `Droppy(0)` is dropped before `Droppy(1)` because its temporary scope is -limited by Edition 2024 to the proper end of the `if let` expression. - -Second, temporary values generated from evaluating the tail expression of a block -or a function body will be dropped earlier than the local variable bindings. -The following example demonstrates the Edition 2024 order of events that values -and local variables are dropped. - -```rust,edition2024 -struct Droppy(u8); -impl Droppy { - fn get(&self) -> Option<&u8> { - Some(&self.0) - } -} -impl Drop for Droppy { - fn drop(&mut self) { - println!("drop({})", self.0); - } -} - -fn call() -> u8 { - let x = Droppy(0); - *x.get() + *Droppy(1).get() -} - -call(); -``` - -The print-out from this program will be the following. - -```text -drop(1) -drop(0) -``` - -In other words, `Droppy(1)` is dropped earlier than `x`. - ### Operands r[destructors.scope.operands] From dbc9380b258d95038db507eea7b4cc44906d3564 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 22 Nov 2024 10:16:03 -0800 Subject: [PATCH 3/5] Drop note about tail temporaries dropping after locals This is no longer true in 2024 --- src/destructors.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 0bfa5f38b..d4ad3727d 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -216,10 +216,6 @@ smallest scope that contains the expression and is one of the following: > **Notes**: > -> Temporaries that are created in the final expression of a function -> body are dropped *after* any named variables bound in the function body. -> Their drop scope is the entire function, 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 From 4eddade5fbdfb549db5fcc2be57c4ec0f641c4ff Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 22 Nov 2024 10:17:15 -0800 Subject: [PATCH 4/5] Rework 2024 temporary edition differences The style of the reference is to document the latest edition in the main body of the text, and any differences in a separate block. As a compromise, I included links to the edition description. --- src/destructors.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index d4ad3727d..123866d10 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -211,8 +211,8 @@ smallest scope that contains the expression and is one of the following: guard. * The body expression for a match arm. * Each operand of a [lazy boolean expression]. -* *Starting from Edition 2024* the pattern-matching condition and consequent body of [`if let`]. -* *Starting from Edition 2024* the entirety of the tail expression of a block. +* The pattern-matching condition and consequent body of [`if let`] ([destructors.scope.temporary.edition2024]). +* The entirety of the tail expression of a block ([destructors.scope.temporary.edition2024]). > **Notes**: > @@ -221,6 +221,9 @@ smallest scope that contains the expression and is one of the following: > example, the temporary for `1` in `match 1 { ref mut z => z };` lives until > the end of the statement. +r[destructors.scope.temporary.edition2024] +> **Edition differences**: The 2024 edition added two new temporary scope narrowing rules: `if let` temporaries are dropped before the `else` block, and temporaries of tail expressions of blocks are dropped immediately after the tail expression is evaluated. + Some examples: ```rust From 7d62543f1f3a2d643693dbf72489e11625073bd9 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 22 Nov 2024 10:19:28 -0800 Subject: [PATCH 5/5] Update the example for 2024 temporary drop --- src/destructors.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 123866d10..36c677135 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -226,6 +226,7 @@ r[destructors.scope.temporary.edition2024] Some examples: + ```rust # struct PrintOnDrop(&'static str); # impl Drop for PrintOnDrop { @@ -243,6 +244,16 @@ if PrintOnDrop("If condition").0 == "If condition" { 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 +}; + // Dropped before the first || (PrintOnDrop("first operand").0 == "" // Dropped before the ) @@ -250,10 +261,8 @@ if PrintOnDrop("If condition").0 == "If condition" { // Dropped before the ; || 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. +// 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 == "" => (),