-
Notifications
You must be signed in to change notification settings - Fork 537
if let
guards documentation
#1823
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
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 |
---|---|---|
|
@@ -126,6 +126,15 @@ r[destructors.scope.nesting.match-arm] | |
* The parent of the expression after the `=>` in a `match` expression is the | ||
scope of the arm that it's in. | ||
|
||
r[destructors.scope.nesting.match-guard-pattern] | ||
* The parent of an `if let` guard pattern is the scope of the guard expression. | ||
|
||
r[destructors.scope.nesting.match-guard-bindings] | ||
* Variables bound by `if let` guard patterns have their drop scope determined by | ||
guard success: | ||
- On guard failure: dropped immediately after guard evaluation | ||
- On guard success: scope extends to the end of the match arm body | ||
Comment on lines
+132
to
+136
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. I'm not sure I understand the need for this rule. Since the bindings are scoped to the match arm, I would expect destructors.scope.nesting.match-guard to cover this. That is, as we leave the arm (success or failure), the bindings will get dropped. Can you clarify how this would be different from the other rule? 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. This one also can be removed by the same reason as above 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. But I'm unsure where should I write about drop on failure case, because they are different on success and failure, I think we can keep it, but I'm not sure where exactly 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. I can't speak to the spec, but implementation-wise, there's no difference between success and failure. fn main() {
assert_drop_order(1..=3, |o| match () {
() if let (_, _) = (o.log(2), o.log(3))
&& true =>
{
o.log(1);
}
_ => unreachable!(),
});
} On both success and failure, the reason The difference between the guard's bindings and the match arm's pattern's bindings is all in sequencing. By-value bindings for the match arm's pattern are only created if the guard succeeds (expr.match.guard.value). When a guard fails, the guard's scrutinee and bindings/temporaries from earlier in the EDIT: Updated the example to actually demonstrate the scrutinee's temporary scope. I guess the drop order for the tuple is because tuples drop the left field first, then the right? Whereas if you create bindings for the fields, the right one is dropped first, then left. |
||
|
||
r[destructors.scope.nesting.match] | ||
* The parent of the arm scope is the scope of the `match` expression that it | ||
belongs to. | ||
|
@@ -204,8 +213,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 condition expression of an `if` or `while` expression. | ||
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. Why is this changing 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. i forgot to return it actually, but after this change i realise that we should keep them separatly actually, something like this would be better?
|
||
* A `match` guard expression, including `if let` guard patterns. | ||
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. I'm a little confused by this. Don't 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. I believe the temporary scope of an |
||
* The body expression for a match arm. | ||
* Each operand of a [lazy boolean expression]. | ||
* The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]). | ||
|
@@ -415,6 +424,58 @@ let x = (&temp()).use_temp(); // ERROR | |
# x; | ||
``` | ||
|
||
r[destructors.scope.match-guards] | ||
### Match Guards and Pattern Binding | ||
Comment on lines
+427
to
+428
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. Overall I'm uncertain what this section is saying that is different from the temporary scope rules in destructors.scope.temporary.enclosing. I'm also unclear about why this is differentiating if the pattern matches or not. Either case is handled when execution leaves the arm (success or failure), right? 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. i guess im over-engineering documentation, i will try to look on it from different angle |
||
|
||
r[destructors.scope.match-guards.basic] | ||
Match guard expressions create their own temporary scope. Variables bound within | ||
guard patterns have conditional drop scopes based on guard evaluation results. | ||
|
||
r[destructors.scope.match-guards.if-let] | ||
For `if let` guards specifically: | ||
|
||
1. **Guard pattern evaluation**: The `if let` pattern is evaluated within the | ||
guard's temporary scope. | ||
2. **Conditional binding scope**: | ||
- If the pattern matches, bound variables extend their scope to the match arm body | ||
- If the pattern fails, bound variables are dropped immediately | ||
3. **Temporary cleanup**: Temporaries created during guard evaluation are dropped | ||
when the guard scope ends, regardless of pattern match success. | ||
|
||
r[destructors.scope.match-guards.multiple] | ||
For multiple guards connected by `&&`: | ||
- Each guard maintains its own temporary scope | ||
- Failed guards drop their bindings before subsequent guard evaluation | ||
- Only successful guard bindings are available in the arm body | ||
- Guards are evaluated left-to-right with short-circuit semantics | ||
|
||
```rust | ||
# struct PrintOnDrop(&'static str); | ||
# impl Drop for PrintOnDrop { | ||
# fn drop(&mut self) { | ||
# println!("drop({})", self.0); | ||
# } | ||
# } | ||
# fn expensive_operation(x: i32) -> Option<PrintOnDrop> { | ||
# Some(PrintOnDrop("expensive result")) | ||
# } | ||
|
||
match Some(42) { | ||
// Guard creates temporary scope for pattern evaluation | ||
Some(x) if let Some(y) = expensive_operation(x) => { | ||
// Both x (from main pattern) and y (from guard) are live | ||
// y will be dropped at end of this arm | ||
println!("Success case"); | ||
} // y dropped here | ||
Some(x) => { | ||
// If guard failed, y was already dropped during guard evaluation | ||
// expensive_operation result was also dropped | ||
println!("Guard failed case"); | ||
} | ||
None => {} | ||
} | ||
``` | ||
|
||
r[destructors.forget] | ||
## Not running destructors | ||
|
||
|
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.
I'm struggling with this one. Isn't the parent of both the scrutinee and the bindings of an
if let
guard the entire arm? I'm not able to find a way that it is different from destructors.scope.nesting.match-guard.For example, in:
I would expect the parent of both
one
andsome_expression
to be the match arm (and drop after the arm body).Can you clarify this for me?
Uh oh!
There was an error while loading. Please reload this page.
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.
Hm, yes, we can delete this because it duplucate of destructors.scope.nesting.match-guard, or at least alligns in a way it shouldnt