Skip to content

Commit bd2f4b2

Browse files
committed
Add let_chains references
1 parent 5c4a7a6 commit bd2f4b2

File tree

2 files changed

+108
-63
lines changed

2 files changed

+108
-63
lines changed

src/expressions/if-expr.md

+71-52
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
# `if` and `if let` expressions
1+
# `if` expressions
22

3-
## `if` expressions
3+
## Syntax
4+
5+
The same syntax is used by `if`, `if let` and chains of expressions.
46

57
> **<sup>Syntax</sup>**\
68
> _IfExpression_ :\
7-
> &nbsp;&nbsp; `if` [_Expression_]<sub>_except struct expression_</sub> [_BlockExpression_]\
8-
> &nbsp;&nbsp; (`else` (
9-
> [_BlockExpression_]
10-
> | _IfExpression_
11-
> | _IfLetExpression_ ) )<sup>\?</sup>
9+
> &nbsp;&nbsp; `if` _IfLetList_ [_BlockExpression_]\
10+
> &nbsp;&nbsp; ( `else` _IfLetList_ [_BlockExpression_] )<sup>\?</sup>
11+
>
12+
> _IfLet_ :\
13+
> &nbsp;&nbsp; [_Expression_]<sub>_except struct expression_</sub>
14+
> | `let` [_Pattern_] `=` [_Expression_]<sub>_except struct expression_</sub>
15+
>
16+
> _IfLetList_ :\
17+
> &nbsp;&nbsp; _IfLet_ ( && _IfLet_ )*
18+
19+
## `if`
1220

1321
An `if` expression is a conditional branch in program control.
1422
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.
@@ -37,16 +45,7 @@ let y = if 12 * 15 > 150 {
3745
assert_eq!(y, "Bigger");
3846
```
3947

40-
## `if let` expressions
41-
42-
> **<sup>Syntax</sup>**\
43-
> _IfLetExpression_ :\
44-
> &nbsp;&nbsp; `if` `let` [_Pattern_] `=` [_Scrutinee_]<sub>_except lazy boolean operator expression_</sub>
45-
> [_BlockExpression_]\
46-
> &nbsp;&nbsp; (`else` (
47-
> [_BlockExpression_]
48-
> | _IfExpression_
49-
> | _IfLetExpression_ ) )<sup>\?</sup>
48+
## `if let`
5049

5150
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.
5251
If the value of the scrutinee matches the pattern, the corresponding block will execute.
@@ -90,27 +89,6 @@ let a = if let Some(1) = x {
9089
assert_eq!(a, 3);
9190
```
9291

93-
An `if let` expression is equivalent to a [`match` expression] as follows:
94-
95-
<!-- ignore: expansion example -->
96-
```rust,ignore
97-
if let PATS = EXPR {
98-
/* body */
99-
} else {
100-
/*else */
101-
}
102-
```
103-
104-
is equivalent to
105-
106-
<!-- ignore: expansion example -->
107-
```rust,ignore
108-
match EXPR {
109-
PATS => { /* body */ },
110-
_ => { /* else */ }, // () if there is no else
111-
}
112-
```
113-
11492
Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions:
11593

11694
```rust
@@ -125,31 +103,72 @@ if let E::X(n) | E::Y(n) = v {
125103
}
126104
```
127105

128-
The expression cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_].
129-
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_]).
130-
When lazy boolean operator expression is desired, this can be achieved by using parenthesis as below:
106+
The expression cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. Scrutinee expressions also cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_] due to ambiguity and precedence with [chains of expressions][_ChainsOfExpressions_].
131107

132-
<!-- ignore: psuedo code -->
133-
```rust,ignore
134-
// Before...
135-
if let PAT = EXPR && EXPR { .. }
108+
## Chains of expressions
109+
110+
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:
136111

137-
// After...
138-
if let PAT = ( EXPR && EXPR ) { .. }
112+
```rust
113+
fn single() {
114+
let outer_opt = Some(Some(1i32));
115+
116+
if let Some(inner_opt) = outer_opt
117+
&& let Some(number) = inner_opt
118+
&& number == 1
119+
{
120+
println!("Peek a boo");
121+
}
122+
}
123+
124+
The above is equivalent to the following without using expression chains:
125+
126+
fn nested() {
127+
let outer_opt = Some(Some(1i32));
128+
129+
if let Some(inner_opt) = outer_opt {
130+
if let Some(number) = inner_opt {
131+
if number == 1 {
132+
println!("Peek a boo");
133+
}
134+
}
135+
}
136+
}
137+
```
138+
139+
In other words, chains are equivalent to nested [`if let` expressions]:
140+
141+
<!-- ignore: expansion example -->
142+
```rust,ignore
143+
if let PAT0 = EXPR0 && let PAT1 = EXPR1 {
144+
/* body */
145+
} else {
146+
/* else */
147+
}
148+
```
139149

140-
// Before...
141-
if let PAT = EXPR || EXPR { .. }
150+
is equivalent to
142151

143-
// After...
144-
if let PAT = ( EXPR || EXPR ) { .. }
152+
<!-- ignore: expansion example -->
153+
```rust,ignore
154+
if let PAT0 = EXPR0 {
155+
if let PAT1 = EXPR1 {
156+
/* body */
157+
}
158+
else {
159+
/* else */
160+
}
161+
} else {
162+
/* else */
163+
}
145164
```
146165

147166
[_BlockExpression_]: block-expr.md
167+
[_ChainsOfExpressions_]: #chains-of-expressions
148168
[_Expression_]: ../expressions.md
149169
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
150170
[_Pattern_]: ../patterns.md
151171
[_Scrutinee_]: match-expr.md
152-
[_eRFCIfLetChain_]: https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md#rollout-plan-and-transitioning-to-rust-2018
153172
[`match` expression]: match-expr.md
154173
[boolean type]: ../types/boolean.md
155174
[scrutinee]: ../glossary.md#scrutinee

src/expressions/loop-expr.md

+37-11
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
> &nbsp;&nbsp; [_LoopLabel_]<sup>?</sup> (\
66
> &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; [_InfiniteLoopExpression_]\
77
> &nbsp;&nbsp; &nbsp;&nbsp; | [_PredicateLoopExpression_]\
8-
> &nbsp;&nbsp; &nbsp;&nbsp; | [_PredicatePatternLoopExpression_]\
98
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IteratorLoopExpression_]\
109
> &nbsp;&nbsp; )
1110
1211
[_LoopLabel_]: #loop-labels
1312
[_InfiniteLoopExpression_]: #infinite-loops
1413
[_PredicateLoopExpression_]: #predicate-loops
15-
[_PredicatePatternLoopExpression_]: #predicate-pattern-loops
1614
[_IteratorLoopExpression_]: #iterator-loops
1715

1816
Rust supports four loop expressions:
@@ -39,9 +37,23 @@ A `loop` expression containing associated [`break` expression(s)](#break-express
3937

4038
## Predicate loops
4139

40+
### Syntax
41+
42+
The same syntax is used by `while`, `while let` and chains of expressions.
43+
4244
> **<sup>Syntax</sup>**\
43-
> _PredicateLoopExpression_ :\
44-
> &nbsp;&nbsp; `while` [_Expression_]<sub>_except struct expression_</sub> [_BlockExpression_]
45+
> [_PredicateLoopExpression_] :\
46+
> _WhileExpression_ :\
47+
> &nbsp;&nbsp; `while` _WhileLetList_ [_BlockExpression_]
48+
>
49+
> _WhileLet_ :\
50+
> &nbsp;&nbsp; ( [_Expression_]<sub>_except struct expression_</sub>
51+
> | `let` [_Pattern_] `=` [_Expression_]<sub>_except struct expression_</sub> )
52+
>
53+
> _WhileLetList_ :\
54+
> &nbsp;&nbsp; _WhileLet_ ( && _WhileLet_ )*
55+
56+
### `while`
4557

4658
A `while` loop begins by evaluating the [boolean] loop conditional operand.
4759
If the loop conditional operand evaluates to `true`, the loop body block executes, then control returns to the loop conditional operand.
@@ -58,13 +70,7 @@ while i < 10 {
5870
}
5971
```
6072

61-
## Predicate pattern loops
62-
63-
> **<sup>Syntax</sup>**\
64-
> [_PredicatePatternLoopExpression_] :\
65-
> &nbsp;&nbsp; `while` `let` [_Pattern_] `=` [_Scrutinee_]<sub>_except lazy boolean operator expression_</sub>
66-
> [_BlockExpression_]
67-
73+
### `while let`
6874

6975
A `while let` loop is semantically similar to a `while` loop but in place of a condition expression it expects the keyword `let` followed by a pattern, an `=`, a [scrutinee] expression and a block expression.
7076
If the value of the scrutinee matches the pattern, the loop body block executes then control returns to the pattern matching statement.
@@ -117,6 +123,26 @@ while let Some(v @ 1) | Some(v @ 2) = vals.pop() {
117123

118124
As is the case in [`if let` expressions], the scrutinee cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_].
119125

126+
### Chains of expressions
127+
128+
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:
129+
130+
```rust
131+
fn main() {
132+
let outer_opt = Some(Some(1i32));
133+
134+
while let Some(inner_opt) = outer_opt
135+
&& let Some(number) = inner_opt
136+
&& number == 1
137+
{
138+
println!("Peek a boo");
139+
break;
140+
}
141+
}
142+
```
143+
144+
The only remark is the fact that parenthesis (`while (let Some(a) = opt && (let Some(b) = a)) && b == 1`) and `||` operators (`while let A(x) = e1 || let B(x) = e2`) are not currently supported.
145+
120146
## Iterator loops
121147

122148
> **<sup>Syntax</sup>**\

0 commit comments

Comments
 (0)