From 4662059acd4ab5dffe888a31256320347b0692a5 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 25 Jul 2022 08:14:36 -0300 Subject: [PATCH 1/2] Add let_chains references --- src/expressions/if-expr.md | 122 ++++++++++++++++++++--------------- src/expressions/loop-expr.md | 48 ++++++++++---- 2 files changed, 108 insertions(+), 62 deletions(-) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index 591437fc9..c82cad3d1 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 @@ -126,30 +104,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: +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**\ From b83d8e616c47e11cfc152618e1b633311ffa29ad Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 25 Jul 2022 15:40:07 -0700 Subject: [PATCH 2/2] 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. --- src/SUMMARY.md | 2 +- src/attributes/type_system.md | 1 - src/const_eval.md | 6 +- src/destructors.md | 4 +- src/expressions.md | 6 +- src/expressions/block-expr.md | 3 +- src/expressions/if-expr.md | 112 +++++++++++++-------------------- src/expressions/loop-expr.md | 62 +++++++++--------- src/expressions/struct-expr.md | 2 +- src/names.md | 4 +- src/names/namespaces.md | 4 +- src/patterns.md | 2 +- src/tokens.md | 4 +- 13 files changed, 91 insertions(+), 121 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 82d70d043..6a8f72475 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -62,7 +62,7 @@ - [Closure expressions](expressions/closure-expr.md) - [Loop expressions](expressions/loop-expr.md) - [Range expressions](expressions/range-expr.md) - - [If and if let expressions](expressions/if-expr.md) + - [If expressions](expressions/if-expr.md) - [Match expressions](expressions/match-expr.md) - [Return expressions](expressions/return-expr.md) - [Await expressions](expressions/await-expr.md) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 729069d26..d15836fb0 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -133,7 +133,6 @@ Non-exhaustive types are always considered inhabited in downstream crates. [_StructExpression_]: ../expressions/struct-expr.md [_StructPattern_]: ../patterns.md#struct-patterns [_TupleStructPattern_]: ../patterns.md#tuple-struct-patterns -[`if let`]: ../expressions/if-expr.md#if-let-expressions [`match`]: ../expressions/match-expr.md [attributes]: ../attributes.md [enum]: ../items/enumerations.md diff --git a/src/const_eval.md b/src/const_eval.md index c0560376c..d735a20ec 100644 --- a/src/const_eval.md +++ b/src/const_eval.md @@ -45,8 +45,8 @@ to be run. * pointer to address casts and * function pointer to address casts. * Calls of [const functions] and const methods. -* [loop], [while] and [`while let`] expressions. -* [if], [`if let`] and [match] expressions. +* [loop] and [while] expressions. +* [if] and [match] expressions. ## Const context @@ -121,7 +121,6 @@ Conversely, the following are possible in a const function, but not in a const c [grouped]: expressions/grouped-expr.md [interior mutability]: interior-mutability.md [if]: expressions/if-expr.md#if-expressions -[`if let`]: expressions/if-expr.md#if-let-expressions [lazy boolean]: expressions/operator-expr.md#lazy-boolean-operators [let statements]: statements.md#let-statements [literals]: expressions/literal-expr.md @@ -138,4 +137,3 @@ Conversely, the following are possible in a const function, but not in a const c [struct]: expressions/struct-expr.md [tuple expressions]: expressions/tuple-expr.md [while]: expressions/loop-expr.md#predicate-loops -[`while let`]: expressions/loop-expr.md#predicate-pattern-loops diff --git a/src/destructors.md b/src/destructors.md index 6d616b3e7..083ed86eb 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -388,12 +388,12 @@ variable or field from being dropped automatically. [tuple expression]: expressions/tuple-expr.md#tuple-expressions [`for`]: expressions/loop-expr.md#iterator-loops -[`if let`]: expressions/if-expr.md#if-let-expressions +[`if let`]: expressions/if-expr.md#if-let-patterns [`if`]: expressions/if-expr.md#if-expressions [`let` statement]: statements.md#let-statements [`loop`]: expressions/loop-expr.md#infinite-loops [`match`]: expressions/match-expr.md -[`while let`]: expressions/loop-expr.md#predicate-pattern-loops +[`while let`]: expressions/loop-expr.md#while-let-patterns [`while`]: expressions/loop-expr.md#predicate-loops [`::drop`]: ../std/ops/trait.Drop.html#tymethod.drop diff --git a/src/expressions.md b/src/expressions.md index 32ee658ff..5b6525fec 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -38,7 +38,6 @@ >       | [_UnsafeBlockExpression_]\ >       | [_LoopExpression_]\ >       | [_IfExpression_]\ ->       | [_IfLetExpression_]\ >       | [_MatchExpression_]\ >    ) @@ -295,13 +294,13 @@ They are never allowed before: [call expressions]: expressions/call-expr.md [field]: expressions/field-expr.md [functional update]: expressions/struct-expr.md#functional-update-syntax -[`if let`]: expressions/if-expr.md#if-let-expressions +[`if let`]: expressions/if-expr.md#if-let-patterns [match]: expressions/match-expr.md [method-call]: expressions/method-call-expr.md [paths]: expressions/path-expr.md [struct]: expressions/struct-expr.md [tuple expressions]: expressions/tuple-expr.md -[`while let`]: expressions/loop-expr.md#predicate-pattern-loops +[`while let`]: expressions/loop-expr.md#while-let-patterns [array expressions]: expressions/array-expr.md [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions @@ -348,7 +347,6 @@ They are never allowed before: [_FieldExpression_]: expressions/field-expr.md [_GroupedExpression_]: expressions/grouped-expr.md [_IfExpression_]: expressions/if-expr.md#if-expressions -[_IfLetExpression_]: expressions/if-expr.md#if-let-expressions [_IndexExpression_]: expressions/array-expr.md#array-and-slice-indexing-expressions [_LazyBooleanExpression_]: expressions/operator-expr.md#lazy-boolean-operators [_LiteralExpression_]: expressions/literal-expr.md diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index a68b27e56..01e6ef95c 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -145,7 +145,7 @@ let a = unsafe { an_unsafe_fn() }; [Inner attributes] are allowed directly after the opening brace of a block expression in the following situations: * [Function] and [method] bodies. -* Loop bodies ([`loop`], [`while`], [`while let`], and [`for`]). +* Loop bodies ([`loop`], [`while`], and [`for`]). * Block expressions used as a [statement]. * Block expressions as elements of [array expressions], [tuple expressions], [call expressions], and tuple-style [struct] expressions. @@ -172,7 +172,6 @@ fn is_unix_platform() -> bool { [`loop`]: loop-expr.md#infinite-loops [`std::ops::Fn`]: ../../std/ops/trait.Fn.html [`std::future::Future`]: ../../std/future/trait.Future.html -[`while let`]: loop-expr.md#predicate-pattern-loops [`while`]: loop-expr.md#predicate-loops [array expressions]: array-expr.md [call expressions]: call-expr.md diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index c82cad3d1..9de65df87 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -2,29 +2,30 @@ ## Syntax -The same syntax is used by `if`, `if let` and chains of expressions. - > **Syntax**\ > _IfExpression_ :\ ->    `if` _IfLetList_ [_BlockExpression_]\ ->    ( `else` _IfLetList_ [_BlockExpression_] )\? +>    `if` _IfConditions_ [_BlockExpression_]\ +>    (`else` ( [_BlockExpression_] | _IfExpression_ ) )\? > -> _IfLet_ :\ ->    [_Expression_]_except struct expression_ -> | `let` [_Pattern_] `=` [_Expression_]_except struct expression_ +> _IfConditions_ :\ +>    _IfCondition_ ( && _IfCondition_ )* > -> _IfLetList_ :\ ->    _IfLet_ ( && _IfLet_ )* - -## `if` +> _IfCondition_ :\ +>       [_Expression_]_except struct expression_\ +>    | `let` [_Pattern_] `=` [_Scrutinee_] 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. -The condition operands must have the [boolean type]. -If a condition operand evaluates to `true`, the consequent block is executed and any subsequent `else if` or `else` block is skipped. -If a condition operand evaluates to `false`, the consequent block is skipped and any subsequent `else if` condition is evaluated. +The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`, +followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block. + +Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match. +If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s, +the consequent block is executed and any subsequent `else if` or `else` block is skipped. +If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee, +the consequent block is skipped and any subsequent `else if` condition is evaluated. If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed. -An if expression evaluates to the same value as the executed block, or `()` if no block is evaluated. + +An `if` expression evaluates to the same value as the executed block, or `()` if no block is evaluated. An `if` expression must have the same type in all situations. ```rust @@ -37,6 +38,7 @@ if x == 4 { println!("x is something else"); } +// `if` can be used as an expression. let y = if 12 * 15 > 150 { "Bigger" } else { @@ -45,17 +47,15 @@ let y = if 12 * 15 > 150 { assert_eq!(y, "Bigger"); ``` -## `if let` +## `if let` patterns -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. -Otherwise, flow proceeds to the following `else` block if it exists. -Like `if` expressions, `if let` expressions have a value determined by the block that is evaluated. +`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully. +The following examples illustrate bindings using `let` patterns: ```rust let dish = ("Ham", "Eggs"); -// this body will be skipped because the pattern is refuted +// This body will be skipped because the pattern is refuted. if let ("Bacon", b) = dish { println!("Bacon is served with {}", b); } else { @@ -63,7 +63,7 @@ if let ("Bacon", b) = dish { println!("No bacon will be served"); } -// this body will execute +// This body will execute. if let ("Ham", b) = dish { println!("Ham is served with {}", b); } @@ -73,23 +73,8 @@ if let _ = 5 { } ``` -`if` and `if let` expressions can be intermixed: - -```rust -let x = Some(3); -let a = if let Some(1) = x { - 1 -} else if x == Some(2) { - 2 -} else if let Some(y) = x { - y -} else { - -1 -}; -assert_eq!(a, 3); -``` - -Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions: +Multiple patterns may be specified with the `|` operator. +This has the same semantics as with `|` in [`match` expressions]: ```rust enum E { @@ -103,10 +88,13 @@ if let E::X(n) | E::Y(n) = v { } ``` -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_]. +## Chains of conditions -## Chains of expressions +Multiple condition operands can be separated with `&&`. +Similar to a `&&` [_LazyBooleanOperatorExpression_], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails, +in which case the subsequent operands are not evaluated. + +The bindings of each pattern are put into scope to be available for the next condition operand and the consequent block. 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: @@ -121,9 +109,11 @@ fn single() { println!("Peek a boo"); } } +``` -The above is equivalent to the following without using expression chains: +The above is equivalent to the following without using chains of conditions: +```rust fn nested() { let outer_opt = Some(Some(1i32)); @@ -137,39 +127,23 @@ fn nested() { } ``` -In other words, chains are equivalent to nested [`if let` expressions]: +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. +If a `||` expression is needed, then parentheses can be used. For example: - -```rust,ignore -if let PAT0 = EXPR0 && let PAT1 = EXPR1 { - /* body */ -} else { - /* else */ -} +```rust +# let foo = Some(123); +# let condition1 = true; +# let condition2 = false; +// Parentheses are required here. +if let Some(x) = foo && (condition1 || condition2) { /*...*/ } ``` -is equivalent to - - -```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 -[`match` expression]: match-expr.md +[`match` expressions]: 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 5033a1ce8..d9458d56f 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -13,14 +13,13 @@ [_PredicateLoopExpression_]: #predicate-loops [_IteratorLoopExpression_]: #iterator-loops -Rust supports four loop expressions: +Rust supports three loop expressions: * A [`loop` expression](#infinite-loops) denotes an infinite loop. * A [`while` expression](#predicate-loops) loops until a predicate is false. -* A [`while let` expression](#predicate-pattern-loops) tests a pattern. * A [`for` expression](#iterator-loops) extracts values from an iterator, looping until the iterator is empty. -All four types of loop support [`break` expressions](#break-expressions), [`continue` expressions](#continue-expressions), and [labels](#loop-labels). +All three types of loop support [`break` expressions](#break-expressions), [`continue` expressions](#continue-expressions), and [labels](#loop-labels). Only `loop` supports [evaluation to non-trivial values](#break-and-loop-values). ## Infinite loops @@ -37,27 +36,29 @@ 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_] :\ -> _WhileExpression_ :\ ->    `while` _WhileLetList_ [_BlockExpression_] +>    `while` _WhileConditions_ [_BlockExpression_] > -> _WhileLet_ :\ ->    ( [_Expression_]_except struct expression_ -> | `let` [_Pattern_] `=` [_Expression_]_except struct expression_ ) +> _WhileConditions_ :\ +>    _WhileCondition_ ( && _WhileCondition_ )* > -> _WhileLetList_ :\ ->    _WhileLet_ ( && _WhileLet_ )* +> _WhileCondition_ :\ +>       [_Expression_]_except struct expression_\ +>    | `let` [_Pattern_] `=` [_Scrutinee_] -### `while` +A `while` loop expression allows repeating the evaluation of a block while a set of conditions remain true. +The syntax of a `while` expression is a sequence of one or more condition operands separated by `&&`, +followed by a [_BlockExpression_]. -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. -If the loop conditional expression evaluates to `false`, the `while` expression completes. +Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match. +If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s, +then the loop body block executes. +After the loop body successfully executes, the condition operands are re-evaluated to determine if the body should be executed again. +If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee, +the body is not executed and execution continues after the `while` expression. + +A `while` expression evaluates to `()`. An example: @@ -70,11 +71,10 @@ while i < 10 { } ``` -### `while let` +### `while let` patterns -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. -Otherwise, the while expression completes. +`let` patterns in a `while` condition allow binding new variables into scope when the pattern matches successfully. +The following examples illustrate bindings using `let` patterns: ```rust let mut x = vec![1, 2, 3]; @@ -121,9 +121,12 @@ 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_]. +As is the case in [`if` expressions], the scrutinee cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. + +### `while` condition chains -### Chains of expressions +Multiple condition operands can be separated with `&&`. +These have the same semantics and restrictions as [`if` condition chains]. 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: @@ -141,8 +144,6 @@ fn main() { } ``` -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**\ @@ -259,7 +260,7 @@ A `break` expression is only permitted in the body of a loop, and has one of the >    `continue` [LIFETIME_OR_LABEL]? When `continue` is encountered, the current iteration of the associated loop body is immediately terminated, returning control to the loop *head*. -In the case of a `while` loop, the head is the conditional expression controlling the loop. +In the case of a `while` loop, the head is the conditional operands controlling the loop. In the case of a `for` loop, the head is the call-expression controlling the loop. Like `break`, `continue` is normally associated with the innermost enclosing loop, but `continue 'label` may be used to specify the loop affected. @@ -290,11 +291,12 @@ In the case a `loop` has an associated `break`, it is not considered diverging, [LIFETIME_OR_LABEL]: ../tokens.md#lifetimes-and-loop-labels [_BlockExpression_]: block-expr.md [_Expression_]: ../expressions.md +[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators [_Pattern_]: ../patterns.md [_Scrutinee_]: match-expr.md +[`if` condition chains]: if-expr.md#chains-of-conditions +[`if` expressions]: if-expr.md [`match` expression]: match-expr.md -[boolean]: ../types/boolean.md +[boolean type]: ../types/boolean.md [scrutinee]: ../glossary.md#scrutinee [temporary values]: ../expressions.md#temporaries -[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators -[`if let` expressions]: if-expr.md#if-let-expressions diff --git a/src/expressions/struct-expr.md b/src/expressions/struct-expr.md index 8caeff200..e0643fe2a 100644 --- a/src/expressions/struct-expr.md +++ b/src/expressions/struct-expr.md @@ -131,7 +131,7 @@ let b = Gamma{}; // Exact same value as `a`. [_PathInExpression_]: ../paths.md#paths-in-expressions [call expression]: call-expr.md [enum variant]: ../items/enumerations.md -[if let]: if-expr.md#if-let-expressions +[if let]: if-expr.md#if-let-patterns [if]: if-expr.md#if-expressions [loop]: loop-expr.md [match]: match-expr.md diff --git a/src/names.md b/src/names.md index fd8f50cd0..26bce0bd1 100644 --- a/src/names.md +++ b/src/names.md @@ -90,13 +90,13 @@ to with certain [path qualifiers] or aliases. [*visibility*]: visibility-and-privacy.md [`'static`]: keywords.md#weak-keywords [`for`]: expressions/loop-expr.md#iterator-loops -[`if let`]: expressions/if-expr.md#if-let-expressions +[`if let`]: expressions/if-expr.md#if-let-patterns [`let` statement]: statements.md#let-statements [`macro_export` attribute]: macros-by-example.md#path-based-scope [`macro_rules` declarations]: macros-by-example.md [`macro_use` attribute]: macros-by-example.md#the-macro_use-attribute [`match`]: expressions/match-expr.md -[`while let`]: expressions/loop-expr.md#predicate-pattern-loops +[`while let`]: expressions/loop-expr.md#while-let-patterns [associated items]: items/associated-items.md [attributes]: attributes.md [Boolean type]: types/boolean.md diff --git a/src/names/namespaces.md b/src/names/namespaces.md index 14811697c..1c94745d8 100644 --- a/src/names/namespaces.md +++ b/src/names/namespaces.md @@ -118,14 +118,14 @@ It is still an error for a [`use` import] to shadow another macro, regardless of [`cfg` attribute]: ../conditional-compilation.md#the-cfg-attribute [`cfg` macro]: ../conditional-compilation.md#the-cfg-macro [`for`]: ../expressions/loop-expr.md#iterator-loops -[`if let`]: ../expressions/if-expr.md#if-let-expressions +[`if let`]: ../expressions/if-expr.md#if-let-patterns [`let`]: ../statements.md#let-statements [`macro_rules` declarations]: ../macros-by-example.md [`match`]: ../expressions/match-expr.md [`Self` constructors]: ../paths.md#self-1 [`Self` type]: ../paths.md#self-1 [`use` import]: ../items/use-declarations.md -[`while let`]: ../expressions/loop-expr.md#predicate-pattern-loops +[`while let`]: ../expressions/loop-expr.md#while-let-patterns [Associated const declarations]: ../items/associated-items.md#associated-constants [Associated function declarations]: ../items/associated-items.md#associated-functions-and-methods [Associated type declarations]: ../items/associated-items.md#associated-types diff --git a/src/patterns.md b/src/patterns.md index 5c81ecd87..3a1c609bf 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -69,7 +69,7 @@ Patterns are used in: parameters * [`match` expressions](expressions/match-expr.md) * [`if let` expressions](expressions/if-expr.md) -* [`while let` expressions](expressions/loop-expr.md#predicate-pattern-loops) +* [`while let` expressions](expressions/loop-expr.md#while-let-patterns) * [`for` expressions](expressions/loop-expr.md#iterator-loops) ## Destructuring diff --git a/src/tokens.md b/src/tokens.md index 197c20147..71a3527fd 100644 --- a/src/tokens.md +++ b/src/tokens.md @@ -710,7 +710,7 @@ Similarly the `r`, `b`, and `br` prefixes used in raw string literals, byte lite [functions]: items/functions.md [generics]: items/generics.md [identifier]: identifiers.md -[if let]: expressions/if-expr.md#if-let-expressions +[if let]: expressions/if-expr.md#if-let-patterns [keywords]: keywords.md [lazy-bool]: expressions/operator-expr.md#lazy-boolean-operators [literal expressions]: expressions/literal-expr.md @@ -738,4 +738,4 @@ Similarly the `r`, `b`, and `br` prefixes used in raw string literals, byte lite [unary minus operator]: expressions/operator-expr.md#negation-operators [use declarations]: items/use-declarations.md [use wildcards]: items/use-declarations.md -[while let]: expressions/loop-expr.md#predicate-pattern-loops +[while let]: expressions/loop-expr.md#while-let-patterns