Skip to content

Commit 3551c46

Browse files
committed
Unify while/while let and if/if let expressions.
This updates the `if` and `while` expressions so that they each are presented as a single expression kind with multiple condition operators instead of being logically separated from their `let` counterparts. This also includes various fixes and additions.
1 parent 68f3e4a commit 3551c46

13 files changed

+91
-121
lines changed

src/SUMMARY.md

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

src/attributes/type_system.md

-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ Non-exhaustive types are always considered inhabited in downstream crates.
133133
[_StructExpression_]: ../expressions/struct-expr.md
134134
[_StructPattern_]: ../patterns.md#struct-patterns
135135
[_TupleStructPattern_]: ../patterns.md#tuple-struct-patterns
136-
[`if let`]: ../expressions/if-expr.md#if-let-expressions
137136
[`match`]: ../expressions/match-expr.md
138137
[attributes]: ../attributes.md
139138
[enum]: ../items/enumerations.md

src/const_eval.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ to be run.
4545
* pointer to address casts and
4646
* function pointer to address casts.
4747
* Calls of [const functions] and const methods.
48-
* [loop], [while] and [`while let`] expressions.
49-
* [if], [`if let`] and [match] expressions.
48+
* [loop] and [while] expressions.
49+
* [if] and [match] expressions.
5050

5151
## Const context
5252

@@ -121,7 +121,6 @@ Conversely, the following are possible in a const function, but not in a const c
121121
[grouped]: expressions/grouped-expr.md
122122
[interior mutability]: interior-mutability.md
123123
[if]: expressions/if-expr.md#if-expressions
124-
[`if let`]: expressions/if-expr.md#if-let-expressions
125124
[lazy boolean]: expressions/operator-expr.md#lazy-boolean-operators
126125
[let statements]: statements.md#let-statements
127126
[literals]: expressions/literal-expr.md
@@ -138,4 +137,3 @@ Conversely, the following are possible in a const function, but not in a const c
138137
[struct]: expressions/struct-expr.md
139138
[tuple expressions]: expressions/tuple-expr.md
140139
[while]: expressions/loop-expr.md#predicate-loops
141-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops

src/destructors.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,12 @@ variable or field from being dropped automatically.
388388
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
389389

390390
[`for`]: expressions/loop-expr.md#iterator-loops
391-
[`if let`]: expressions/if-expr.md#if-let-expressions
391+
[`if let`]: expressions/if-expr.md#if-let-patterns
392392
[`if`]: expressions/if-expr.md#if-expressions
393393
[`let` statement]: statements.md#let-statements
394394
[`loop`]: expressions/loop-expr.md#infinite-loops
395395
[`match`]: expressions/match-expr.md
396-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops
396+
[`while let`]: expressions/loop-expr.md#while-let-patterns
397397
[`while`]: expressions/loop-expr.md#predicate-loops
398398

399399
[`<T as std::ops::Drop>::drop`]: ../std/ops/trait.Drop.html#tymethod.drop

src/expressions.md

+2-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
> &nbsp;&nbsp; &nbsp;&nbsp; | [_UnsafeBlockExpression_]\
3939
> &nbsp;&nbsp; &nbsp;&nbsp; | [_LoopExpression_]\
4040
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IfExpression_]\
41-
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IfLetExpression_]\
4241
> &nbsp;&nbsp; &nbsp;&nbsp; | [_MatchExpression_]\
4342
> &nbsp;&nbsp; )
4443
@@ -295,13 +294,13 @@ They are never allowed before:
295294
[call expressions]: expressions/call-expr.md
296295
[field]: expressions/field-expr.md
297296
[functional update]: expressions/struct-expr.md#functional-update-syntax
298-
[`if let`]: expressions/if-expr.md#if-let-expressions
297+
[`if let`]: expressions/if-expr.md#if-let-patterns
299298
[match]: expressions/match-expr.md
300299
[method-call]: expressions/method-call-expr.md
301300
[paths]: expressions/path-expr.md
302301
[struct]: expressions/struct-expr.md
303302
[tuple expressions]: expressions/tuple-expr.md
304-
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops
303+
[`while let`]: expressions/loop-expr.md#while-let-patterns
305304

306305
[array expressions]: expressions/array-expr.md
307306
[array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions
@@ -348,7 +347,6 @@ They are never allowed before:
348347
[_FieldExpression_]: expressions/field-expr.md
349348
[_GroupedExpression_]: expressions/grouped-expr.md
350349
[_IfExpression_]: expressions/if-expr.md#if-expressions
351-
[_IfLetExpression_]: expressions/if-expr.md#if-let-expressions
352350
[_IndexExpression_]: expressions/array-expr.md#array-and-slice-indexing-expressions
353351
[_LazyBooleanExpression_]: expressions/operator-expr.md#lazy-boolean-operators
354352
[_LiteralExpression_]: expressions/literal-expr.md

src/expressions/block-expr.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ let a = unsafe { an_unsafe_fn() };
145145
[Inner attributes] are allowed directly after the opening brace of a block expression in the following situations:
146146

147147
* [Function] and [method] bodies.
148-
* Loop bodies ([`loop`], [`while`], [`while let`], and [`for`]).
148+
* Loop bodies ([`loop`], [`while`], and [`for`]).
149149
* Block expressions used as a [statement].
150150
* Block expressions as elements of [array expressions], [tuple expressions],
151151
[call expressions], and tuple-style [struct] expressions.
@@ -172,7 +172,6 @@ fn is_unix_platform() -> bool {
172172
[`loop`]: loop-expr.md#infinite-loops
173173
[`std::ops::Fn`]: ../../std/ops/trait.Fn.html
174174
[`std::future::Future`]: ../../std/future/trait.Future.html
175-
[`while let`]: loop-expr.md#predicate-pattern-loops
176175
[`while`]: loop-expr.md#predicate-loops
177176
[array expressions]: array-expr.md
178177
[call expressions]: call-expr.md

src/expressions/if-expr.md

+43-69
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@
22

33
## Syntax
44

5-
The same syntax is used by `if`, `if let` and chains of expressions.
6-
75
> **<sup>Syntax</sup>**\
86
> _IfExpression_ :\
9-
> &nbsp;&nbsp; `if` _IfLetList_ [_BlockExpression_]\
10-
> &nbsp;&nbsp; ( `else` _IfLetList_ [_BlockExpression_] )<sup>\?</sup>
7+
> &nbsp;&nbsp; `if` _IfConditions_ [_BlockExpression_]\
8+
> &nbsp;&nbsp; (`else` ( [_BlockExpression_] | _IfExpression_ ) )<sup>\?</sup>
119
>
12-
> _IfLet_ :\
13-
> &nbsp;&nbsp; [_Expression_]<sub>_except struct expression_</sub>
14-
> | `let` [_Pattern_] `=` [_Expression_]<sub>_except struct expression_</sub>
10+
> _IfConditions_ :\
11+
> &nbsp;&nbsp; _IfCondition_ ( && _IfCondition_ )*
1512
>
16-
> _IfLetList_ :\
17-
> &nbsp;&nbsp; _IfLet_ ( && _IfLet_ )*
18-
19-
## `if`
13+
> _IfCondition_ :\
14+
> &nbsp;&nbsp; &nbsp;&nbsp; [_Expression_]<sub>_except struct expression_</sub>\
15+
> &nbsp;&nbsp; | `let` [_Pattern_] `=` [_Scrutinee_]
2016
2117
An `if` expression is a conditional branch in program control.
22-
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.
23-
The condition operands must have the [boolean type].
24-
If a condition operand evaluates to `true`, the consequent block is executed and any subsequent `else if` or `else` block is skipped.
25-
If a condition operand evaluates to `false`, the consequent block is skipped and any subsequent `else if` condition is evaluated.
18+
The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`,
19+
followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block.
20+
21+
Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match.
22+
If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s,
23+
the consequent block is executed and any subsequent `else if` or `else` block is skipped.
24+
If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee,
25+
the consequent block is skipped and any subsequent `else if` condition is evaluated.
2626
If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed.
27-
An if expression evaluates to the same value as the executed block, or `()` if no block is evaluated.
27+
28+
An `if` expression evaluates to the same value as the executed block, or `()` if no block is evaluated.
2829
An `if` expression must have the same type in all situations.
2930

3031
```rust
@@ -37,6 +38,7 @@ if x == 4 {
3738
println!("x is something else");
3839
}
3940

41+
// `if` can be used as an expression.
4042
let y = if 12 * 15 > 150 {
4143
"Bigger"
4244
} else {
@@ -45,25 +47,23 @@ let y = if 12 * 15 > 150 {
4547
assert_eq!(y, "Bigger");
4648
```
4749

48-
## `if let`
50+
## `if let` patterns
4951

50-
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.
51-
If the value of the scrutinee matches the pattern, the corresponding block will execute.
52-
Otherwise, flow proceeds to the following `else` block if it exists.
53-
Like `if` expressions, `if let` expressions have a value determined by the block that is evaluated.
52+
`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully.
53+
The following examples illustrate bindings using `let` patterns:
5454

5555
```rust
5656
let dish = ("Ham", "Eggs");
5757

58-
// this body will be skipped because the pattern is refuted
58+
// This body will be skipped because the pattern is refuted.
5959
if let ("Bacon", b) = dish {
6060
println!("Bacon is served with {}", b);
6161
} else {
6262
// This block is evaluated instead.
6363
println!("No bacon will be served");
6464
}
6565

66-
// this body will execute
66+
// This body will execute.
6767
if let ("Ham", b) = dish {
6868
println!("Ham is served with {}", b);
6969
}
@@ -73,23 +73,8 @@ if let _ = 5 {
7373
}
7474
```
7575

76-
`if` and `if let` expressions can be intermixed:
77-
78-
```rust
79-
let x = Some(3);
80-
let a = if let Some(1) = x {
81-
1
82-
} else if x == Some(2) {
83-
2
84-
} else if let Some(y) = x {
85-
y
86-
} else {
87-
-1
88-
};
89-
assert_eq!(a, 3);
90-
```
91-
92-
Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions:
76+
Multiple patterns may be specified with the `|` operator.
77+
This has the same semantics as with `|` in [`match` expressions]:
9378

9479
```rust
9580
enum E {
@@ -103,10 +88,13 @@ if let E::X(n) | E::Y(n) = v {
10388
}
10489
```
10590

106-
The expression cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_].
107-
Scrutinee expressions also cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_] due to ambiguity and precedence with [chains of expressions][_ChainsOfExpressions_].
91+
## Chains of conditions
10892

109-
## Chains of expressions
93+
Multiple condition operands can be separated with `&&`.
94+
Similar to a `&&` [_LazyBooleanOperatorExpression_], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails,
95+
in which case the subsequent operands are not evaluated.
96+
97+
The bindings of each pattern are put into scope to be available for the next condition operand and the consequent block.
11098

11199
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:
112100

@@ -121,9 +109,11 @@ fn single() {
121109
println!("Peek a boo");
122110
}
123111
}
112+
```
124113

125-
The above is equivalent to the following without using expression chains:
114+
The above is equivalent to the following without using chains of conditions:
126115

116+
```rust
127117
fn nested() {
128118
let outer_opt = Some(Some(1i32));
129119

@@ -137,39 +127,23 @@ fn nested() {
137127
}
138128
```
139129

140-
In other words, chains are equivalent to nested [`if let` expressions]:
130+
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.
131+
If a `||` expression is needed, then parentheses can be used. For example:
141132

142-
<!-- ignore: expansion example -->
143-
```rust,ignore
144-
if let PAT0 = EXPR0 && let PAT1 = EXPR1 {
145-
/* body */
146-
} else {
147-
/* else */
148-
}
133+
```rust
134+
# let foo = Some(123);
135+
# let condition1 = true;
136+
# let condition2 = false;
137+
// Parentheses are required here.
138+
if let Some(x) = foo && (condition1 || condition2) { /*...*/ }
149139
```
150140

151-
is equivalent to
152-
153-
<!-- ignore: expansion example -->
154-
```rust,ignore
155-
if let PAT0 = EXPR0 {
156-
if let PAT1 = EXPR1 {
157-
/* body */
158-
}
159-
else {
160-
/* else */
161-
}
162-
} else {
163-
/* else */
164-
}
165-
```
166141

167142
[_BlockExpression_]: block-expr.md
168-
[_ChainsOfExpressions_]: #chains-of-expressions
169143
[_Expression_]: ../expressions.md
170144
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
171145
[_Pattern_]: ../patterns.md
172146
[_Scrutinee_]: match-expr.md
173-
[`match` expression]: match-expr.md
147+
[`match` expressions]: match-expr.md
174148
[boolean type]: ../types/boolean.md
175149
[scrutinee]: ../glossary.md#scrutinee

0 commit comments

Comments
 (0)