-
Notifications
You must be signed in to change notification settings - Fork 562
specify if let guards with updated scoping rules
#1957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
483902e
8ce1dc6
12fd5b7
e69fef8
f1b193c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,11 +165,8 @@ patterns_in_parameters( | |
| r[destructors.scope.bindings] | ||
| ### Scopes of local variables | ||
|
|
||
| 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. | ||
| r[destructors.scope.bindings.let] | ||
| Local variables declared in a `let` statement are associated to the scope of the block that contains the `let` statement. | ||
|
|
||
| ```rust | ||
| # struct PrintOnDrop(&'static str); | ||
|
|
@@ -185,6 +182,49 @@ let declared_first = PrintOnDrop("Dropped last in outer scope"); | |
| let declared_last = PrintOnDrop("Dropped first in outer scope"); | ||
| ``` | ||
|
|
||
| r[destructors.scope.bindings.match-arm] | ||
| Local variables declared in a `match` expression or pattern-matching `match` guard are associated to the arm scope of the `match` arm that they are declared in. | ||
|
|
||
| ```rust | ||
| # #![allow(irrefutable_let_patterns)] | ||
| # struct PrintOnDrop(&'static str); | ||
| # impl Drop for PrintOnDrop { | ||
| # fn drop(&mut self) { | ||
| # println!("drop({})", self.0); | ||
| # } | ||
| # } | ||
| match PrintOnDrop("Dropped last in the first arm's scope") { | ||
| // When guard evaluation succeeds, control-flow stays in the arm and | ||
| // values may be moved from the scrutinee into the arm's bindings, | ||
| // causing them to be dropped in the arm's scope. | ||
| x if let y = PrintOnDrop("Dropped second in the first arm's scope") | ||
| && let z = PrintOnDrop("Dropped first in the first arm's scope") => | ||
| { | ||
| let declared_in_block = PrintOnDrop("Dropped in inner scope"); | ||
| // Pattern-matching guards' bindings and temporaries are dropped in | ||
| // reverse order, dropping each guard condition operand's bindings | ||
| // before its temporaries. Lastly, variables bound by the arm's | ||
| // pattern are dropped. | ||
| } | ||
| _ => unreachable!(), | ||
| } | ||
|
|
||
| match PrintOnDrop("Dropped in the enclosing temporary scope") { | ||
| // When guard evaluation fails, control-flow leaves the arm scope, | ||
| // causing bindings and temporaries from earlier pattern-matching | ||
| // guard condition operands to be dropped. This occurs before evaluating | ||
| // the next arm's guard or body. | ||
| _ if let y = PrintOnDrop("Dropped in the first arm's scope") | ||
| && false => unreachable!(), | ||
| // When a guard is executed multiple times due to self-overlapping | ||
| // or-patterns, control-flow leaves the arm scope when the guard fails | ||
| // and re-enters the arm scope before executing the guard again. | ||
| _ | _ if let y = PrintOnDrop("Dropped in the second arm's scope twice") | ||
| && false => unreachable!(), | ||
| _ => {}, | ||
| } | ||
| ``` | ||
|
|
||
| r[destructors.scope.bindings.patterns] | ||
| Variables in patterns are dropped in reverse order of declaration within the pattern. | ||
|
|
||
|
|
@@ -255,9 +295,8 @@ smallest scope that contains the expression and is one of the following: | |
| * A statement. | ||
| * The body of an [`if`], [`while`] or [`loop`] expression. | ||
| * The `else` block of an `if` expression. | ||
| * The non-pattern matching condition expression of an `if` or `while` expression, | ||
| or a `match` guard. | ||
| * The body expression for a match arm. | ||
| * The non-pattern matching condition expression of an `if` or `while` expression or a non-pattern-matching `match` [guard condition operand]. | ||
| * The pattern-matching guard, if present, and body expression for a `match` arm. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To address #1823 (comment) (cc @theemathas), the temporary lifetime of an
This comment was marked as resolved.
Sorry, something went wrong.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, are you saying that both the guard and the body expression together count as a single temporary scope (and not two)? I find that unintuitive, but I suppose that works.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a single temporary scope for the arm, which encompasses the guard and body expression, so that Personally, I'd like stricter scoping rules for |
||
| * Each operand of a [lazy boolean expression]. | ||
| * The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]). | ||
| * The pattern-matching condition and loop body of [`while`]. | ||
|
|
@@ -317,8 +356,16 @@ while let x = PrintOnDrop("while let scrutinee").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 | ||
| // Non-pattern-matching guards' temporaries are dropped once the | ||
| // condition has been evaluated | ||
| _ if PrintOnDrop("guard condition").0 == "" => (), | ||
| // Pattern-matching guards' temporaries are dropped when leaving the | ||
| // arm's scope | ||
| _ if let "guard scrutinee" = PrintOnDrop("guard scrutinee").0 => { | ||
| let _ = &PrintOnDrop("lifetime-extended temporary in inner scope"); | ||
| // `lifetime-extended temporary in inner scope` is dropped here | ||
| } | ||
| // `guard scrutinee` is dropped here | ||
| _ => (), | ||
| } | ||
| ``` | ||
|
|
@@ -502,6 +549,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi | |
| [closure]: types/closure.md | ||
| [destructors]: destructors.md | ||
| [expression]: expressions.md | ||
| [guard condition operand]: expressions/match-expr.md#match-guard-chains | ||
| [identifier pattern]: patterns.md#identifier-patterns | ||
| [initialized]: glossary.md#initialized | ||
| [interior mutability]: interior-mutability.md | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing the struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed: (diff)