Skip to content

Commit fa42259

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

File tree

2 files changed

+86
-54
lines changed

2 files changed

+86
-54
lines changed

src/destructors.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,50 @@ let x = (&temp()).use_temp(); // ERROR
415415
# x;
416416
```
417417

418+
r[destructors.scope.temporary.if-let-guard]
419+
### If Let Guards in Match Expressions
420+
421+
When an `if let` guard is evaluated in a match expression:
422+
423+
1. **Guard evaluation scope**: Variables bound by the `if let` pattern and any temporaries created during guard evaluation have a scope that extends through the match arm body if the guard succeeds.
424+
425+
2. **Failed guard cleanup**: If the `if let` guard fails to match:
426+
- Any variables bound by the guard pattern are immediately dropped
427+
- Temporaries created during guard evaluation are dropped
428+
- Pattern matching continues to the next arm
429+
430+
3. **Successful guard**: If the guard succeeds:
431+
- Guard-bound variables remain live for the duration of the match arm body
432+
- They are dropped at the end of the match arm execution
433+
434+
4. **Multiple guards**: In arms with multiple guards connected by `&&`:
435+
- Each failed guard drops its own bindings before evaluating the next guard
436+
- Only successful guard bindings are available in the arm body
437+
438+
#### Example
439+
440+
```rust
441+
# let value = Some(2)
442+
# fn expensive_operation(x: i32) -> Option<i32> {
443+
# Some(x + 2)
444+
# }
445+
#
446+
match value {
447+
Some(x) if let Some(y) = expensive_operation(x) => {
448+
// Both x and y are live here
449+
// expensive_operation(x) result is moved into y
450+
// y dropped here at end of arm
451+
} // Any temporaries from expensive_operation dropped here
452+
Some(x) => {
453+
// If first guard failed, y and expensive_operation result
454+
// were already dropped before reaching this arm
455+
}
456+
None => {}
457+
}
458+
```
459+
460+
This behavior is consistent across all Rust editions and matches the scoping semantics of nested `if let` expressions within match arms.
461+
418462
r[destructors.forget]
419463
## Not running destructors
420464

src/expressions/match-expr.md

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,8 @@ MatchArmGuard ->
2323
`if` MatchConditions
2424
2525
MatchConditions ->
26-
MatchCondition ( `&&` MatchCondition )*
27-
28-
MatchCondition ->
29-
OuterAttribute* `let` Pattern `=` Scrutinee
30-
| Expression
26+
Expression _except [StructExpression]_
27+
| LetChain
3128
```
3229
<!-- TODO: The exception above isn't accurate, see https://github.com/rust-lang/reference/issues/569 -->
3330

@@ -161,71 +158,62 @@ r[expr.match.guard.no-mutation]
161158
Moreover, by holding a shared reference while evaluating the guard, mutation inside guards is also prevented.
162159
163160
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.
161+
## If-let Guards
162+
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.
166163
167164
r[expr.match.if.let.guard.syntax]
168-
```rust,ignore
169-
match expression {
170-
pattern if let subpattern = guard_expr => arm_body,
171-
...
165+
```rust
166+
enum Command {
167+
Run(String),
168+
Stop,
169+
}
170+
171+
match cmd {
172+
Command::Run(name) if let Some(first_char) = name.chars().next() => {
173+
// Both `name` and `first_char` are available here
174+
println!("Running: {} (starts with '{}')", name, first_char);
175+
}
176+
Command::Run(name) => {
177+
println!("Cannot run command: {}", name);
178+
}
179+
_ => {}
172180
}
173181
```
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.
182+
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.
183+
184+
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.
175185

176186
r[expr.match.if.let.guard.behavior]
177187
When the pattern matches successfully, the `if let` expression in the guard is evaluated:
178188
* The guard proceeds if the inner pattern (`subpattern`) matches the result of `guard_expr`.
179189
* Otherwise, the next arm is tested.
180190

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.
193-
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);
202-
}
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
191+
```rust
192+
# let value = Some(2)
193+
# fn process(x: i32) -> Result<i32, String> {
194+
# Ok(x + 2)
195+
# }
196+
#
197+
match value {
198+
Some(x) if let Ok(y) = process(x) => {
199+
// ^ ^
200+
// | |
201+
// | +-- `y` bound in if-let guard
202+
// +-- `x` bound in match arm pattern
203+
//
204+
// Both `x` and `y` are available here
205+
println!("Processed {} into {}", x, y);
207206
}
207+
_ => {}
208208
}
209-
210-
// Outside the match expression, neither `x` nor `y` are in scope.
211209
```
212210

213-
* The outer pattern variables (`x`) follow the same borrowing behavior as in standard match guards (see below).
214-
215-
r[expr.match.if.let.guard.drop]
216-
217-
* Variables bound inside `if let` guards are dropped before evaluating subsequent match arms.
218-
219-
* Temporaries created during guard evaluation follow standard drop semantics and are cleaned up appropriately.
211+
r[expr.match.if.let.guard.scope]
220212

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
213+
For detailed information about variable scope and drop behavior, see the [scope and drop section](../destructors.md#If-Let-Guards-in-Match-Expressions).
226214

227215
```rust,ignore
228-
fn take<T>(value: T) -> Option<T> { Some(value) }
216+
# fn take<T>(value: T) -> Option<T> { Some(value) }
229217
230218
let val = Some(vec![1, 2, 3]);
231219
match val {
@@ -236,7 +224,7 @@ match val {
236224
```
237225
> [!NOTE]
238226
> 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
227+
> ```rust
240228
> use std::cell::Cell;
241229
>
242230
> let i: Cell<i32> = Cell::new(0);

0 commit comments

Comments
 (0)