Skip to content

Commit b7317ce

Browse files
committed
cutted down match-expr, added info in destructors, fixed syntax, removed ingore from some blocks
1 parent 19de488 commit b7317ce

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
@@ -126,6 +126,15 @@ r[destructors.scope.nesting.match-arm]
126126
* The parent of the expression after the `=>` in a `match` expression is the
127127
scope of the arm that it's in.
128128

129+
r[destructors.scope.nesting.match-guard-pattern]
130+
* The parent of an `if let` guard pattern is the scope of the guard expression.
131+
132+
r[destructors.scope.nesting.match-guard-bindings]
133+
* Variables bound by `if let` guard patterns have their drop scope determined by
134+
guard success:
135+
- On guard failure: dropped immediately after guard evaluation
136+
- On guard success: scope extends to the end of the match arm body
137+
129138
r[destructors.scope.nesting.match]
130139
* The parent of the arm scope is the scope of the `match` expression that it
131140
belongs to.
@@ -204,8 +213,8 @@ smallest scope that contains the expression and is one of the following:
204213
* A statement.
205214
* The body of an [`if`], [`while`] or [`loop`] expression.
206215
* The `else` block of an `if` expression.
207-
* The non-pattern matching condition expression of an `if` or `while` expression,
208-
or a `match` guard.
216+
* The condition expression of an `if` or `while` expression.
217+
* A `match` guard expression, including `if let` guard patterns.
209218
* The body expression for a match arm.
210219
* Each operand of a [lazy boolean expression].
211220
* The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]).
@@ -415,6 +424,58 @@ let x = (&temp()).use_temp(); // ERROR
415424
# x;
416425
```
417426

427+
r[destructors.scope.match-guards]
428+
### Match Guards and Pattern Binding
429+
430+
r[destructors.scope.match-guards.basic]
431+
Match guard expressions create their own temporary scope. Variables bound within
432+
guard patterns have conditional drop scopes based on guard evaluation results.
433+
434+
r[destructors.scope.match-guards.if-let]
435+
For `if let` guards specifically:
436+
437+
1. **Guard pattern evaluation**: The `if let` pattern is evaluated within the
438+
guard's temporary scope.
439+
2. **Conditional binding scope**:
440+
- If the pattern matches, bound variables extend their scope to the match arm body
441+
- If the pattern fails, bound variables are dropped immediately
442+
3. **Temporary cleanup**: Temporaries created during guard evaluation are dropped
443+
when the guard scope ends, regardless of pattern match success.
444+
445+
r[destructors.scope.match-guards.multiple]
446+
For multiple guards connected by `&&`:
447+
- Each guard maintains its own temporary scope
448+
- Failed guards drop their bindings before subsequent guard evaluation
449+
- Only successful guard bindings are available in the arm body
450+
- Guards are evaluated left-to-right with short-circuit semantics
451+
452+
```rust
453+
# struct PrintOnDrop(&'static str);
454+
# impl Drop for PrintOnDrop {
455+
# fn drop(&mut self) {
456+
# println!("drop({})", self.0);
457+
# }
458+
# }
459+
# fn expensive_operation(x: i32) -> Option<PrintOnDrop> {
460+
# Some(PrintOnDrop("expensive result"))
461+
# }
462+
463+
match Some(42) {
464+
// Guard creates temporary scope for pattern evaluation
465+
Some(x) if let Some(y) = expensive_operation(x) => {
466+
// Both x (from main pattern) and y (from guard) are live
467+
// y will be dropped at end of this arm
468+
println!("Success case");
469+
} // y dropped here
470+
Some(x) => {
471+
// If guard failed, y was already dropped during guard evaluation
472+
// expensive_operation result was also dropped
473+
println!("Guard failed case");
474+
}
475+
None => {}
476+
}
477+
```
478+
418479
r[destructors.forget]
419480
## Not running destructors
420481

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)