Skip to content

Commit 8ce1dc6

Browse files
Kivooeodianne
authored andcommitted
cutted down match-expr, added info in destructors, fixed syntax, removed ingore from some blocks
1 parent 483902e commit 8ce1dc6

File tree

3 files changed

+108
-58
lines changed

3 files changed

+108
-58
lines changed

src/destructors.md

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ r[destructors.scope.nesting.match-arm]
127127
* The parent of the expression after the `=>` in a `match` expression is the
128128
scope of the arm that it's in.
129129

130+
r[destructors.scope.nesting.match-guard-pattern]
131+
* The parent of an `if let` guard pattern is the scope of the guard expression.
132+
133+
r[destructors.scope.nesting.match-guard-bindings]
134+
* Variables bound by `if let` guard patterns have their drop scope determined by
135+
guard success:
136+
- On guard failure: dropped immediately after guard evaluation
137+
- On guard success: scope extends to the end of the match arm body
138+
130139
r[destructors.scope.nesting.match]
131140
* The parent of the arm scope is the scope of the `match` expression that it
132141
belongs to.
@@ -255,8 +264,8 @@ smallest scope that contains the expression and is one of the following:
255264
* A statement.
256265
* The body of an [`if`], [`while`] or [`loop`] expression.
257266
* The `else` block of an `if` expression.
258-
* The non-pattern matching condition expression of an `if` or `while` expression,
259-
or a `match` guard.
267+
* The condition expression of an `if` or `while` expression.
268+
* A `match` guard expression, including `if let` guard patterns.
260269
* The body expression for a match arm.
261270
* Each operand of a [lazy boolean expression].
262271
* The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]).
@@ -475,6 +484,58 @@ let x = (&temp()).use_temp(); // ERROR
475484
# x;
476485
```
477486

487+
r[destructors.scope.match-guards]
488+
### Match Guards and Pattern Binding
489+
490+
r[destructors.scope.match-guards.basic]
491+
Match guard expressions create their own temporary scope. Variables bound within
492+
guard patterns have conditional drop scopes based on guard evaluation results.
493+
494+
r[destructors.scope.match-guards.if-let]
495+
For `if let` guards specifically:
496+
497+
1. **Guard pattern evaluation**: The `if let` pattern is evaluated within the
498+
guard's temporary scope.
499+
2. **Conditional binding scope**:
500+
- If the pattern matches, bound variables extend their scope to the match arm body
501+
- If the pattern fails, bound variables are dropped immediately
502+
3. **Temporary cleanup**: Temporaries created during guard evaluation are dropped
503+
when the guard scope ends, regardless of pattern match success.
504+
505+
r[destructors.scope.match-guards.multiple]
506+
For multiple guards connected by `&&`:
507+
- Each guard maintains its own temporary scope
508+
- Failed guards drop their bindings before subsequent guard evaluation
509+
- Only successful guard bindings are available in the arm body
510+
- Guards are evaluated left-to-right with short-circuit semantics
511+
512+
```rust
513+
# struct PrintOnDrop(&'static str);
514+
# impl Drop for PrintOnDrop {
515+
# fn drop(&mut self) {
516+
# println!("drop({})", self.0);
517+
# }
518+
# }
519+
# fn expensive_operation(x: i32) -> Option<PrintOnDrop> {
520+
# Some(PrintOnDrop("expensive result"))
521+
# }
522+
523+
match Some(42) {
524+
// Guard creates temporary scope for pattern evaluation
525+
Some(x) if let Some(y) = expensive_operation(x) => {
526+
// Both x (from main pattern) and y (from guard) are live
527+
// y will be dropped at end of this arm
528+
println!("Success case");
529+
} // y dropped here
530+
Some(x) => {
531+
// If guard failed, y was already dropped during guard evaluation
532+
// expensive_operation result was also dropped
533+
println!("Guard failed case");
534+
}
535+
None => {}
536+
}
537+
```
538+
478539
r[destructors.forget]
479540
## Not running destructors
480541

src/expressions/match-expr.md

Lines changed: 42 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,25 @@ MatchArmGuard ->
2323
`if` MatchConditions
2424
2525
MatchConditions ->
26-
MatchCondition ( `&&` MatchCondition )*
26+
Expression
27+
| MatchConditionChain
2728
28-
MatchCondition ->
29-
OuterAttribute* `let` Pattern `=` Scrutinee
30-
| Expression
29+
MatchConditionChain ->
30+
MatchChainCondition ( `&&` MatchChainCondition )*
31+
32+
MatchChainCondition ->
33+
Expression _except [ExcludedMatchConditions]_
34+
| OuterAttribute* `let` Pattern `=` MatchGuardScrutinee
35+
36+
MatchGuardScrutinee -> Expression _except [ExcludedMatchConditions]_
37+
38+
@root ExcludedMatchConditions ->
39+
LazyBooleanExpression
40+
| RangeExpr
41+
| RangeFromExpr
42+
| RangeInclusiveExpr
43+
| AssignmentExpression
44+
| CompoundAssignmentExpression
3145
```
3246
<!-- TODO: The exception above isn't accurate, see https://github.com/rust-lang/reference/issues/569 -->
3347

@@ -161,71 +175,42 @@ r[expr.match.guard.no-mutation]
161175
Moreover, by holding a shared reference while evaluating the guard, mutation inside guards is also prevented.
162176
163177
r[expr.match.if.let.guard]
164-
## If Let Guards
165-
Match arms can include `if let` guards to allow conditional pattern matching within the guard clause.
178+
## If-let Guards
179+
Match arms can have additional conditions using *if-let guards*. These allow using `if let` expressions within match guard clauses, enabling conditional pattern matching directly in the guard.
166180
167181
r[expr.match.if.let.guard.syntax]
168-
```rust,ignore
169-
match expression {
170-
pattern if let subpattern = guard_expr => arm_body,
171-
...
182+
```rust
183+
enum Command {
184+
Run(String),
185+
Stop,
172186
}
173-
```
174-
Here, `guard_expr` is evaluated and matched against `subpattern`. If the `if let` expression in the guard matches successfully, the arm's body is executed. Otherwise, pattern matching continues to the next arm.
175-
176-
r[expr.match.if.let.guard.behavior]
177-
When the pattern matches successfully, the `if let` expression in the guard is evaluated:
178-
* The guard proceeds if the inner pattern (`subpattern`) matches the result of `guard_expr`.
179-
* Otherwise, the next arm is tested.
180-
181-
```rust,ignore
182-
let value = Some(10);
183-
184-
let msg = match value {
185-
Some(x) if let Some(y) = Some(x - 1) => format!("Matched inner value: {}", y),
186-
_ => "No match".to_string(),
187-
};
188-
```
189-
190-
r[expr.match.if.let.guard.scope]
191-
* The `if let` guard may refer to variables bound by the outer match pattern.
192-
* New variables bound inside the `if let` guard (e.g., `y` in the example above) are available within the body of the match arm where the guard evaluates to `true`, but are not accessible in other arms or outside the match expression.
193187
194-
```rust,ignore
195-
let opt = Some(42);
196-
197-
match opt {
198-
Some(x) if let Some(y) = Some(x + 1) => {
199-
// Both `x` and `y` are available in this arm,
200-
// since the pattern matched and the guard evaluated to true.
201-
println!("x = {}, y = {}", x, y);
188+
match cmd {
189+
Command::Run(name) if let Some(first_char) = name.chars().next() => {
190+
// Both `name` and `first_char` are available here
191+
println!("Running: {} (starts with '{}')", name, first_char);
202192
}
203-
_ => {
204-
// `y` is not available here --- it was only bound inside the guard above.
205-
// Uncommenting the line below will cause a compile-time error:
206-
// println!("{}", y); // error: cannot find value `y` in this scope
193+
Command::Run(name) => {
194+
println!("Cannot run command: {}", name);
207195
}
196+
_ => {}
208197
}
209-
210-
// Outside the match expression, neither `x` nor `y` are in scope.
211198
```
199+
Here, the guard condition `let Some(first_char) = name.chars().next()` is evaluated. If the `if let` expression successfully matches (i.e., the string has at least one character), the arm's body is executed with both `name` and `first_char` available. Otherwise, pattern matching continues to the next arm.
212200

213-
* The outer pattern variables (`x`) follow the same borrowing behavior as in standard match guards (see below).
201+
The key point is that the `if let` guard creates a new binding (`first_char`) that's only available if the guard succeeds, and this binding can be used alongside the original pattern bindings (`name`) in the arm's body.
214202

215-
r[expr.match.if.let.guard.drop]
216-
217-
* Variables bound inside `if let` guards are dropped before evaluating subsequent match arms.
203+
r[expr.match.if.let.guard.behavior]
204+
When the pattern matches successfully, the `if let` expression in the guard is evaluated:
205+
* The guard proceeds if the inner pattern (`subpattern`) matches the result of `guard_expr`.
206+
* Otherwise, the next arm is tested.
218207

219-
* Temporaries created during guard evaluation follow standard drop semantics and are cleaned up appropriately.
208+
r[expr.match.if.let.guard.scope]
220209

221-
r[expr.match.if.let.guard.borrowing]
222-
Variables bound by the outer pattern follow the same borrowing rules as standard match guards:
223-
* A shared reference is taken to pattern variables before guard evaluation
224-
* Values are moved or copied only when the guard succeeds
225-
* Moving from outer pattern variables within the guard is restricted
210+
For detailed information about variable scope and drop behavior, see the [scope and drop section].
226211

227212
```rust,ignore
228-
fn take<T>(value: T) -> Option<T> { Some(value) }
213+
# fn take<T>(value: T) -> Option<T> { Some(value) }
229214
230215
let val = Some(vec![1, 2, 3]);
231216
match val {
@@ -236,7 +221,7 @@ match val {
236221
```
237222
> [!NOTE]
238223
> Multiple matches using the `|` operator can cause the pattern guard and the side effects it has to execute multiple times. For example:
239-
> ```rust,ignore
224+
> ```rust
240225
> use std::cell::Cell;
241226
>
242227
> let i: Cell<i32> = Cell::new(0);
@@ -268,3 +253,4 @@ r[expr.match.attributes.inner]
268253
[Range Pattern]: ../patterns.md#range-patterns
269254
[scrutinee]: ../glossary.md#scrutinee
270255
[value expression]: ../expressions.md#place-expressions-and-value-expressions
256+
[scope and drop section]: ../destructors.md#match-guards-and-pattern-binding

src/names/scopes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ r[names.scopes.pattern-bindings.let-chains]
5454
* [`if let`] and [`while let`] bindings are valid in the following conditions as well as the consequent block.
5555
r[names.scopes.pattern-bindings.match-arm]
5656
* [`match` arms] bindings are within the [match guard] and the match arm expression.
57+
r[names.scopes.pattern-bindings.match-guard-if-let]
58+
* [`if let` guard] bindings are within the guard expression and the match arm expression if the guard succeeds.
5759

5860
r[names.scopes.pattern-bindings.items]
5961
Local variable scopes do not extend into item declarations.
@@ -347,6 +349,7 @@ impl ImplExample {
347349
[`macro_use` prelude]: preludes.md#macro_use-prelude
348350
[`macro_use`]: ../macros-by-example.md#the-macro_use-attribute
349351
[`match` arms]: ../expressions/match-expr.md
352+
[`if let` guard]: ../expressions/match-expr.md#if-let-guards
350353
[`Self`]: ../paths.md#self-1
351354
[Associated consts]: ../items/associated-items.md#associated-constants
352355
[associated items]: ../items/associated-items.md

0 commit comments

Comments
 (0)