Skip to content

Commit ef9e1f7

Browse files
committed
Document let chains again
1 parent 3340922 commit ef9e1f7

14 files changed

+173
-144
lines changed

Diff for: src/SUMMARY.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
- [Closure expressions](expressions/closure-expr.md)
6464
- [Loop expressions](expressions/loop-expr.md)
6565
- [Range expressions](expressions/range-expr.md)
66-
- [If and if let expressions](expressions/if-expr.md)
66+
- [If expressions](expressions/if-expr.md)
6767
- [Match expressions](expressions/match-expr.md)
6868
- [Return expressions](expressions/return-expr.md)
6969
- [Await expressions](expressions/await-expr.md)

Diff for: src/attributes/type_system.md

-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ let _ = EnumWithNonExhaustiveVariants::First as u8;
206206

207207
Non-exhaustive types are always considered inhabited in downstream crates.
208208

209-
[`if let`]: ../expressions/if-expr.md#if-let-expressions
210209
[`match`]: ../expressions/match-expr.md
211210
[attributes]: ../attributes.md
212211
[enum]: ../items/enumerations.md

Diff for: src/const_eval.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ r[const-eval.const-expr.const-fn]
102102
* Calls of [const functions] and const methods.
103103

104104
r[const-eval.const-expr.loop]
105-
* [loop], [while] and [`while let`] expressions.
105+
* [loop] and [while] expressions.
106106

107107
r[const-eval.const-expr.if-match]
108-
* [if], [`if let`] and [match] expressions.
108+
* [if] and [match] expressions.
109+
110+
expressions.")
109111

110112
r[const-eval.const-context]
111113
## Const context
@@ -185,7 +187,6 @@ of whether you are building on a `64` bit or a `32` bit system.
185187
[grouped]: expressions/grouped-expr.md
186188
[interior mutability]: interior-mutability.md
187189
[if]: expressions/if-expr.md#if-expressions
188-
[`if let`]: expressions/if-expr.md#if-let-expressions
189190
[lazy boolean]: expressions/operator-expr.md#lazy-boolean-operators
190191
[let statements]: statements.md#let-statements
191192
[literals]: expressions/literal-expr.md
@@ -202,4 +203,3 @@ of whether you are building on a `64` bit or a `32` bit system.
202203
[struct]: expressions/struct-expr.md
203204
[tuple expressions]: expressions/tuple-expr.md
204205
[while]: expressions/loop-expr.md#predicate-loops
205-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops

Diff for: src/destructors.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ leaves a drop scope all variables associated to that scope are dropped in
6767
reverse order of declaration (for variables) or creation (for temporaries).
6868

6969
r[destructors.scope.desugaring]
70-
Drop scopes are determined after replacing [`for`], [`if let`], and
71-
[`while let`] expressions with the equivalent expressions using [`match`].
70+
Drop scopes can be determined by replacing [`for`], [`if`], and [`while`]
71+
expressions with equivalent expressions using [`match`], [`loop`] and
72+
`break`.
7273

7374
r[destructors.scope.operators]
7475
Overloaded operators are not distinguished from built-in operators and [binding
@@ -203,11 +204,11 @@ smallest scope that contains the expression and is one of the following:
203204
* A statement.
204205
* The body of an [`if`], [`while`] or [`loop`] expression.
205206
* The `else` block of an `if` expression.
206-
* The condition expression of an `if` or `while` expression, or a `match`
207-
guard.
207+
* The non-pattern matching condition expression of an `if` or `while` expression,
208+
or a `match` guard.
208209
* The body expression for a match arm.
209210
* Each operand of a [lazy boolean expression].
210-
* The pattern-matching condition and consequent body of [`if let`] ([destructors.scope.temporary.edition2024]).
211+
* The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]).
211212
* The entirety of the tail expression of a block ([destructors.scope.temporary.edition2024]).
212213

213214
> [!NOTE]
@@ -479,10 +480,10 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
479480
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
480481

481482
[`for`]: expressions/loop-expr.md#iterator-loops
482-
[`if let`]: expressions/if-expr.md#if-let-expressions
483+
[`if let`]: expressions/if-expr.md#if-let-patterns
483484
[`if`]: expressions/if-expr.md#if-expressions
484485
[`let` statement]: statements.md#let-statements
485486
[`loop`]: expressions/loop-expr.md#infinite-loops
486487
[`match`]: expressions/match-expr.md
487-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops
488+
[`while let`]: expressions/loop-expr.md#while-let-patterns
488489
[`while`]: expressions/loop-expr.md#predicate-loops

Diff for: src/expressions.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ ExpressionWithBlock ->
4141
| UnsafeBlockExpression
4242
| LoopExpression
4343
| IfExpression
44-
| IfLetExpression
4544
| MatchExpression
4645
)
4746
```
@@ -311,9 +310,9 @@ They are never allowed before:
311310

312311
[`Copy`]: special-types-and-traits.md#copy
313312
[`Drop`]: special-types-and-traits.md#drop
314-
[`if let`]: expressions/if-expr.md#if-let-expressions
313+
[`if let`]: expressions/if-expr.md#if-let-patterns
315314
[`Sized`]: special-types-and-traits.md#sized
316-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops
315+
[`while let`]: expressions/loop-expr.md#while-let-patterns
317316
[array expressions]: expressions/array-expr.md
318317
[array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions
319318
[assign]: expressions/operator-expr.md#assignment-expressions

Diff for: src/expressions/block-expr.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ r[expr.block.attributes.inner-attributes]
258258
[Inner attributes] are allowed directly after the opening brace of a block expression in the following situations:
259259

260260
* [Function] and [method] bodies.
261-
* Loop bodies ([`loop`], [`while`], [`while let`], and [`for`]).
261+
* Loop bodies ([`loop`], [`while`], and [`for`]).
262262
* Block expressions used as a [statement].
263263
* Block expressions as elements of [array expressions], [tuple expressions],
264264
[call expressions], and tuple-style [struct] expressions.
@@ -282,7 +282,6 @@ fn is_unix_platform() -> bool {
282282
[`for`]: loop-expr.md#iterator-loops
283283
[`loop`]: loop-expr.md#infinite-loops
284284
[`unsafe` blocks]: ../unsafe-keyword.md#unsafe-blocks-unsafe-
285-
[`while let`]: loop-expr.md#predicate-pattern-loops
286285
[`while`]: loop-expr.md#predicate-loops
287286
[array expressions]: array-expr.md
288287
[call expressions]: call-expr.md

Diff for: src/expressions/if-expr.md

+85-87
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
11
r[expr.if]
2-
# `if` and `if let` expressions
3-
4-
## `if` expressions
2+
# `if` expressions
53

64
r[expr.if.syntax]
75
```grammar,expressions
86
IfExpression ->
9-
`if` Expression _except [StructExprStruct]_ BlockExpression
10-
(`else` ( BlockExpression | IfExpression | IfLetExpression ) )?
7+
`if` IfConditions BlockExpression
8+
(`else` ( BlockExpression | IfExpression ) )?
9+
10+
IfConditions -> IfCondition ( `&&` IfCondition )*
11+
12+
IfCondition ->
13+
Expression _except [StructExprStruct]_
14+
| `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_
1115
```
1216
<!-- TODO: The exception above isn't accurate, see https://github.com/rust-lang/reference/issues/569 -->
1317

1418
r[expr.if.intro]
15-
An `if` expression is a conditional branch in program control.
16-
The syntax of an `if` expression is a condition operand, followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block.
19+
The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`,
20+
followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block.
1721

18-
r[expr.if.condition-bool]
19-
The condition operands must have the [boolean type].
22+
r[expr.if.condition]
23+
Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match.
2024

2125
r[expr.if.condition-true]
22-
If a condition operand evaluates to `true`, the consequent block is executed and any subsequent `else if` or `else` block is skipped.
26+
If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s,
27+
the consequent block is executed and any subsequent `else if` or `else` block is skipped.
2328

2429
r[expr.if.else-if]
25-
If a condition operand evaluates to `false`, the consequent block is skipped and any subsequent `else if` condition is evaluated.
30+
If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee,
31+
the consequent block is skipped and any subsequent `else if` condition is evaluated.
2632

2733
r[expr.if.else]
2834
If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed.
2935

3036
r[expr.if.result]
31-
An if expression evaluates to the same value as the executed block, or `()` if no block is evaluated.
37+
An `if` expression evaluates to the same value as the executed block, or `()` if no block is evaluated.
3238

3339
r[expr.if.type]
3440
An `if` expression must have the same type in all situations.
@@ -43,6 +49,7 @@ if x == 4 {
4349
println!("x is something else");
4450
}
4551

52+
// `if` can be used as an expression.
4653
let y = if 12 * 15 > 150 {
4754
"Bigger"
4855
} else {
@@ -52,39 +59,25 @@ assert_eq!(y, "Bigger");
5259
```
5360

5461
r[expr.if.let]
55-
## `if let` expressions
56-
57-
r[expr.if.let.syntax]
58-
```grammar,expressions
59-
IfLetExpression ->
60-
`if` `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ BlockExpression
61-
(`else` ( BlockExpression | IfExpression | IfLetExpression ) )?
62-
```
62+
## `if let` patterns
6363

6464
r[expr.if.let.intro]
65-
An `if let` expression is semantically similar to an `if` expression but in place of a condition operand it expects the keyword `let` followed by a pattern, an `=` and a [scrutinee] operand.
66-
67-
r[expr.if.let.pattern]
68-
If the value of the scrutinee matches the pattern, the corresponding block will execute.
65+
`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully.
6966

70-
r[expr.if.let.else]
71-
Otherwise, flow proceeds to the following `else` block if it exists.
72-
73-
r[expr.if.let.result]
74-
Like `if` expressions, `if let` expressions have a value determined by the block that is evaluated.
67+
The following examples illustrate bindings using `let` patterns:
7568

7669
```rust
7770
let dish = ("Ham", "Eggs");
7871

79-
// this body will be skipped because the pattern is refuted
72+
// This body will be skipped because the pattern is refuted.
8073
if let ("Bacon", b) = dish {
8174
println!("Bacon is served with {}", b);
8275
} else {
8376
// This block is evaluated instead.
8477
println!("No bacon will be served");
8578
}
8679

87-
// this body will execute
80+
// This body will execute.
8881
if let ("Ham", b) = dish {
8982
println!("Ham is served with {}", b);
9083
}
@@ -94,47 +87,9 @@ if let _ = 5 {
9487
}
9588
```
9689

97-
r[expr.if.let.else-if]
98-
`if` and `if let` expressions can be intermixed:
99-
100-
```rust
101-
let x = Some(3);
102-
let a = if let Some(1) = x {
103-
1
104-
} else if x == Some(2) {
105-
2
106-
} else if let Some(y) = x {
107-
y
108-
} else {
109-
-1
110-
};
111-
assert_eq!(a, 3);
112-
```
113-
114-
r[expr.if.let.desugaring]
115-
An `if let` expression is equivalent to a [`match` expression] as follows:
116-
117-
<!-- ignore: expansion example -->
118-
```rust,ignore
119-
if let PATS = EXPR {
120-
/* body */
121-
} else {
122-
/*else */
123-
}
124-
```
125-
126-
is equivalent to
127-
128-
<!-- ignore: expansion example -->
129-
```rust,ignore
130-
match EXPR {
131-
PATS => { /* body */ },
132-
_ => { /* else */ }, // () if there is no else
133-
}
134-
```
135-
13690
r[expr.if.let.or-pattern]
137-
Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions:
91+
Multiple patterns may be specified with the `|` operator.
92+
This has the same semantics as with `|` in [`match` expressions]:
13893

13994
```rust
14095
enum E {
@@ -148,27 +103,70 @@ if let E::X(n) | E::Y(n) = v {
148103
}
149104
```
150105

151-
r[expr.if.let.lazy-bool]
152-
The expression cannot be a [lazy boolean operator expression][expr.bool-logic].
153-
Use of a lazy boolean operator is ambiguous with a planned feature change of the language (the implementation of if-let chains - see [eRFC 2947][_eRFCIfLetChain_]).
154-
When lazy boolean operator expression is desired, this can be achieved by using parenthesis as below:
106+
r[expr.if.chains]
107+
## Chains of conditions
108+
109+
r[expr.if.chains.intro]
110+
Multiple condition operands can be separated with `&&`.
111+
112+
r[expr.if.chains.order]
113+
Similar to a `&&` [_LazyBooleanOperatorExpression_], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails,
114+
in which case the subsequent operands are not evaluated.
115+
116+
r[expr.if.chains.bindings]
117+
The bindings of each pattern are put into scope to be available for the next condition operand and the consequent block.
118+
119+
The following is an example of chaining multiple expressions, mixing `let` bindings and boolean expressions, and with expressions able to reference pattern bindings from previous expressions:
155120

156-
<!-- ignore: pseudo code -->
157-
```rust,ignore
158-
// Before...
159-
if let PAT = EXPR && EXPR { .. }
121+
```rust
122+
fn single() {
123+
let outer_opt = Some(Some(1i32));
124+
125+
if let Some(inner_opt) = outer_opt
126+
&& let Some(number) = inner_opt
127+
&& number == 1
128+
{
129+
println!("Peek a boo");
130+
}
131+
}
132+
```
160133

161-
// After...
162-
if let PAT = ( EXPR && EXPR ) { .. }
134+
The above is equivalent to the following without using chains of conditions:
163135

164-
// Before...
165-
if let PAT = EXPR || EXPR { .. }
136+
```rust
137+
fn nested() {
138+
let outer_opt = Some(Some(1i32));
139+
140+
if let Some(inner_opt) = outer_opt {
141+
if let Some(number) = inner_opt {
142+
if number == 1 {
143+
println!("Peek a boo");
144+
}
145+
}
146+
}
147+
}
148+
```
149+
150+
r[expr.if.chains.or]
151+
If any condition operand is a `let` pattern, then none of the condition operands can be a `||` [lazy boolean operator expression][_LazyBooleanOperatorExpression_] due to ambiguity and precedence with the `let` scrutinee.
152+
If a `||` expression is needed, then parentheses can be used. For example:
166153

167-
// After...
168-
if let PAT = ( EXPR || EXPR ) { .. }
154+
```rust
155+
# let foo = Some(123);
156+
# let condition1 = true;
157+
# let condition2 = false;
158+
// Parentheses are required here.
159+
if let Some(x) = foo && (condition1 || condition2) { /*...*/ }
169160
```
170161

171-
[_eRFCIfLetChain_]: https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md#rollout-plan-and-transitioning-to-rust-2018
172-
[`match` expression]: match-expr.md
162+
r[expr.if.edition2024]
163+
> **Edition differences**: Before the 2024 edition, let chains are not supported and only a single _IfCondition_ is allowed in an `if` expression.
164+
165+
[_BlockExpression_]: block-expr.md
166+
[_Expression_]: ../expressions.md
167+
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
168+
[_Pattern_]: ../patterns.md
169+
[_Scrutinee_]: match-expr.md
170+
[`match` expressions]: match-expr.md
173171
[boolean type]: ../types/boolean.md
174172
[scrutinee]: ../glossary.md#scrutinee

0 commit comments

Comments
 (0)