From bd2f4b248cfc0557ee06a584951d42ab617ba88c Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 25 Jul 2022 08:13:59 -0300 Subject: [PATCH] Add let_chains references --- src/expressions/if-expr.md | 123 ++++++++++++++++++++--------------- src/expressions/loop-expr.md | 48 ++++++++++---- 2 files changed, 108 insertions(+), 63 deletions(-) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index 591437fc9..6443d9fe9 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -1,14 +1,22 @@ -# `if` and `if let` expressions +# `if` expressions -## `if` expressions +## Syntax + +The same syntax is used by `if`, `if let` and chains of expressions. > **Syntax**\ > _IfExpression_ :\ ->    `if` [_Expression_]_except struct expression_ [_BlockExpression_]\ ->    (`else` ( -> [_BlockExpression_] -> | _IfExpression_ -> | _IfLetExpression_ ) )\? +>    `if` _IfLetList_ [_BlockExpression_]\ +>    ( `else` _IfLetList_ [_BlockExpression_] )\? +> +> _IfLet_ :\ +>    [_Expression_]_except struct expression_ +> | `let` [_Pattern_] `=` [_Expression_]_except struct expression_ +> +> _IfLetList_ :\ +>    _IfLet_ ( && _IfLet_ )* + +## `if` An `if` expression is a conditional branch in program control. 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 { assert_eq!(y, "Bigger"); ``` -## `if let` expressions - -> **Syntax**\ -> _IfLetExpression_ :\ ->    `if` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ -> [_BlockExpression_]\ ->    (`else` ( -> [_BlockExpression_] -> | _IfExpression_ -> | _IfLetExpression_ ) )\? +## `if let` 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. 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 { assert_eq!(a, 3); ``` -An `if let` expression is equivalent to a [`match` expression] as follows: - - -```rust,ignore -if let PATS = EXPR { - /* body */ -} else { - /*else */ -} -``` - -is equivalent to - - -```rust,ignore -match EXPR { - PATS => { /* body */ }, - _ => { /* else */ }, // () if there is no else -} -``` - Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions: ```rust @@ -125,31 +103,72 @@ if let E::X(n) | E::Y(n) = v { } ``` -The expression cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. -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_]). -When lazy boolean operator expression is desired, this can be achieved by using parenthesis as below: +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_]. - -```rust,ignore -// Before... -if let PAT = EXPR && EXPR { .. } +## Chains of expressions + +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: -// After... -if let PAT = ( EXPR && EXPR ) { .. } +```rust +fn single() { + let outer_opt = Some(Some(1i32)); + + if let Some(inner_opt) = outer_opt + && let Some(number) = inner_opt + && number == 1 + { + println!("Peek a boo"); + } +} + +The above is equivalent to the following without using expression chains: + +fn nested() { + let outer_opt = Some(Some(1i32)); + + if let Some(inner_opt) = outer_opt { + if let Some(number) = inner_opt { + if number == 1 { + println!("Peek a boo"); + } + } + } +} +``` + +In other words, chains are equivalent to nested [`if let` expressions]: + + +```rust,ignore +if let PAT0 = EXPR0 && let PAT1 = EXPR1 { + /* body */ +} else { + /* else */ +} +``` -// Before... -if let PAT = EXPR || EXPR { .. } +is equivalent to -// After... -if let PAT = ( EXPR || EXPR ) { .. } + +```rust,ignore +if let PAT0 = EXPR0 { + if let PAT1 = EXPR1 { + /* body */ + } + else { + /* else */ + } +} else { + /* else */ +} ``` [_BlockExpression_]: block-expr.md +[_ChainsOfExpressions_]: #chains-of-expressions [_Expression_]: ../expressions.md [_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators [_Pattern_]: ../patterns.md [_Scrutinee_]: match-expr.md -[_eRFCIfLetChain_]: https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md#rollout-plan-and-transitioning-to-rust-2018 [`match` expression]: match-expr.md [boolean type]: ../types/boolean.md [scrutinee]: ../glossary.md#scrutinee diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 308f3e346..5033a1ce8 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -5,14 +5,12 @@ >    [_LoopLabel_]? (\ >          [_InfiniteLoopExpression_]\ >       | [_PredicateLoopExpression_]\ ->       | [_PredicatePatternLoopExpression_]\ >       | [_IteratorLoopExpression_]\ >    ) [_LoopLabel_]: #loop-labels [_InfiniteLoopExpression_]: #infinite-loops [_PredicateLoopExpression_]: #predicate-loops -[_PredicatePatternLoopExpression_]: #predicate-pattern-loops [_IteratorLoopExpression_]: #iterator-loops Rust supports four loop expressions: @@ -39,9 +37,23 @@ A `loop` expression containing associated [`break` expression(s)](#break-express ## Predicate loops +### Syntax + +The same syntax is used by `while`, `while let` and chains of expressions. + > **Syntax**\ -> _PredicateLoopExpression_ :\ ->    `while` [_Expression_]_except struct expression_ [_BlockExpression_] +> [_PredicateLoopExpression_] :\ +> _WhileExpression_ :\ +>    `while` _WhileLetList_ [_BlockExpression_] +> +> _WhileLet_ :\ +>    ( [_Expression_]_except struct expression_ +> | `let` [_Pattern_] `=` [_Expression_]_except struct expression_ ) +> +> _WhileLetList_ :\ +>    _WhileLet_ ( && _WhileLet_ )* + +### `while` A `while` loop begins by evaluating the [boolean] loop conditional operand. 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 { } ``` -## Predicate pattern loops - -> **Syntax**\ -> [_PredicatePatternLoopExpression_] :\ ->    `while` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ -> [_BlockExpression_] - +### `while let` 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. 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() { As is the case in [`if let` expressions], the scrutinee cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. +### Chains of expressions + +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: + +```rust +fn main() { + let outer_opt = Some(Some(1i32)); + + while let Some(inner_opt) = outer_opt + && let Some(number) = inner_opt + && number == 1 + { + println!("Peek a boo"); + break; + } +} +``` + +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. + ## Iterator loops > **Syntax**\