Skip to content

Commit 3873926

Browse files
authored
Merge pull request #1740 from est31/let_chains
Document `let_chains` again
2 parents 07e32fc + 8b5822b commit 3873926

14 files changed

+210
-146
lines changed

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)

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

src/const_eval.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ 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.
109109

110110
r[const-eval.const-context]
111111
## Const context
@@ -185,7 +185,6 @@ of whether you are building on a `64` bit or a `32` bit system.
185185
[grouped]: expressions/grouped-expr.md
186186
[interior mutability]: interior-mutability.md
187187
[if]: expressions/if-expr.md#if-expressions
188-
[`if let`]: expressions/if-expr.md#if-let-expressions
189188
[lazy boolean]: expressions/operator-expr.md#lazy-boolean-operators
190189
[let statements]: statements.md#let-statements
191190
[literals]: expressions/literal-expr.md
@@ -202,4 +201,3 @@ of whether you are building on a `64` bit or a `32` bit system.
202201
[struct]: expressions/struct-expr.md
203202
[tuple expressions]: expressions/tuple-expr.md
204203
[while]: expressions/loop-expr.md#predicate-loops
205-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops

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

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

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

src/expressions/if-expr.md

+116-88
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
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 [StructExpression]_ BlockExpression
10-
(`else` ( BlockExpression | IfExpression | IfLetExpression ) )?
7+
`if` Conditions BlockExpression
8+
(`else` ( BlockExpression | IfExpression ) )?
9+
10+
Conditions ->
11+
Expression _except [StructExpression]_
12+
| LetChain
13+
14+
LetChain -> LetChainCondition ( `&&` LetChainCondition )*
15+
16+
LetChainCondition ->
17+
Expression _except [ExcludedConditions]_
18+
| OuterAttribute* `let` Pattern `=` Scrutinee _except [ExcludedConditions]_
19+
20+
@root ExcludedConditions ->
21+
StructExpression
22+
| LazyBooleanExpression
23+
| RangeExpr
24+
| RangeFromExpr
25+
| RangeInclusiveExpr
26+
| AssignmentExpression
27+
| CompoundAssignmentExpression
1128
```
12-
<!-- TODO: The exception above isn't accurate, see https://github.com/rust-lang/reference/issues/569 -->
29+
<!-- TODO: The struct exception above needs clarification, see https://github.com/rust-lang/reference/issues/1808
30+
The chain grammar could use some work, see https://github.com/rust-lang/reference/issues/1811
31+
-->
1332

1433
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.
34+
The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`,
35+
followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block.
1736

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

2140
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.
41+
If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s,
42+
the consequent block is executed and any subsequent `else if` or `else` block is skipped.
2343

2444
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.
45+
If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee,
46+
the consequent block is skipped and any subsequent `else if` condition is evaluated.
2647

2748
r[expr.if.else]
2849
If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed.
2950

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

3354
r[expr.if.type]
3455
An `if` expression must have the same type in all situations.
@@ -43,6 +64,7 @@ if x == 4 {
4364
println!("x is something else");
4465
}
4566

67+
// `if` can be used as an expression.
4668
let y = if 12 * 15 > 150 {
4769
"Bigger"
4870
} else {
@@ -52,39 +74,25 @@ assert_eq!(y, "Bigger");
5274
```
5375

5476
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-
```
77+
## `if let` patterns
6378

6479
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.
80+
`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully.
6981

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.
82+
The following examples illustrate bindings using `let` patterns:
7583

7684
```rust
7785
let dish = ("Ham", "Eggs");
7886

79-
// this body will be skipped because the pattern is refuted
87+
// This body will be skipped because the pattern is refuted.
8088
if let ("Bacon", b) = dish {
8189
println!("Bacon is served with {}", b);
8290
} else {
8391
// This block is evaluated instead.
8492
println!("No bacon will be served");
8593
}
8694

87-
// this body will execute
95+
// This body will execute.
8896
if let ("Ham", b) = dish {
8997
println!("Ham is served with {}", b);
9098
}
@@ -94,47 +102,9 @@ if let _ = 5 {
94102
}
95103
```
96104

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-
136105
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:
106+
Multiple patterns may be specified with the `|` operator.
107+
This has the same semantics as with `|` in [`match` expressions]:
138108

139109
```rust
140110
enum E {
@@ -148,27 +118,85 @@ if let E::X(n) | E::Y(n) = v {
148118
}
149119
```
150120

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:
121+
r[expr.if.chains]
122+
## Chains of conditions
155123

156-
<!-- ignore: pseudo code -->
157-
```rust,ignore
158-
// Before...
159-
if let PAT = EXPR && EXPR { .. }
124+
r[expr.if.chains.intro]
125+
Multiple condition operands can be separated with `&&`.
160126

161-
// After...
162-
if let PAT = ( EXPR && EXPR ) { .. }
127+
r[expr.if.chains.order]
128+
Similar to a `&&` [_LazyBooleanOperatorExpression_], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails,
129+
in which case the subsequent operands are not evaluated.
163130

164-
// Before...
165-
if let PAT = EXPR || EXPR { .. }
131+
r[expr.if.chains.bindings]
132+
The bindings of each pattern are put into scope to be available for the next condition operand and the consequent block.
166133

167-
// After...
168-
if let PAT = ( EXPR || EXPR ) { .. }
134+
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:
135+
136+
```rust
137+
fn single() {
138+
let outer_opt = Some(Some(1i32));
139+
140+
if let Some(inner_opt) = outer_opt
141+
&& let Some(number) = inner_opt
142+
&& number == 1
143+
{
144+
println!("Peek a boo");
145+
}
146+
}
169147
```
170148

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
149+
The above is equivalent to the following without using chains of conditions:
150+
151+
```rust
152+
fn nested() {
153+
let outer_opt = Some(Some(1i32));
154+
155+
if let Some(inner_opt) = outer_opt {
156+
if let Some(number) = inner_opt {
157+
if number == 1 {
158+
println!("Peek a boo");
159+
}
160+
}
161+
}
162+
}
163+
```
164+
165+
r[expr.if.chains.or]
166+
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.
167+
If a `||` expression is needed, then parentheses can be used. For example:
168+
169+
```rust
170+
# let foo = Some(123);
171+
# let condition1 = true;
172+
# let condition2 = false;
173+
// Parentheses are required here.
174+
if let Some(x) = foo && (condition1 || condition2) { /*...*/ }
175+
```
176+
177+
r[expr.if.edition2024]
178+
> [!EDITION-2024]
179+
> Before the 2024 edition, let chains are not supported. That is, the [LetChain] grammar is not allowed in an `if` expression.
180+
181+
[_BlockExpression_]: block-expr.md
182+
[_Expression_]: ../expressions.md
183+
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
184+
[_Pattern_]: ../patterns.md
185+
[_Scrutinee_]: match-expr.md
186+
[`match` expressions]: match-expr.md
173187
[boolean type]: ../types/boolean.md
174188
[scrutinee]: ../glossary.md#scrutinee
189+
190+
<script>
191+
(function() {
192+
var fragments = {
193+
"#if-let-expressions": "if-expr.html#if-let-patterns",
194+
};
195+
var target = fragments[window.location.hash];
196+
if (target) {
197+
var url = window.location.toString();
198+
var base = url.substring(0, url.lastIndexOf('/'));
199+
window.location.replace(base + "/" + target);
200+
}
201+
})();
202+
</script>

0 commit comments

Comments
 (0)