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 591437fc9..9de65df87 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -1,22 +1,31 @@ -# `if` and `if let` expressions +# `if` expressions -## `if` expressions +## Syntax > **Syntax**\ > _IfExpression_ :\ ->    `if` [_Expression_]_except struct expression_ [_BlockExpression_]\ ->    (`else` ( -> [_BlockExpression_] -> | _IfExpression_ -> | _IfLetExpression_ ) )\? +>    `if` _IfConditions_ [_BlockExpression_]\ +>    (`else` ( [_BlockExpression_] | _IfExpression_ ) )\? +> +> _IfConditions_ :\ +>    _IfCondition_ ( && _IfCondition_ )* +> +> _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 @@ -29,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 { @@ -37,26 +47,15 @@ let y = if 12 * 15 > 150 { assert_eq!(y, "Bigger"); ``` -## `if let` expressions +## `if let` patterns -> **Syntax**\ -> _IfLetExpression_ :\ ->    `if` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ -> [_BlockExpression_]\ ->    (`else` ( -> [_BlockExpression_] -> | _IfExpression_ -> | _IfLetExpression_ ) )\? - -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 { @@ -64,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); } @@ -74,44 +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); -``` - -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: +Multiple patterns may be specified with the `|` operator. +This has the same semantics as with `|` in [`match` expressions]: ```rust enum E { @@ -125,31 +88,62 @@ 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: +## Chains of conditions + +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: + +```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"); + } +} +``` - -```rust,ignore -// Before... -if let PAT = EXPR && EXPR { .. } +The above is equivalent to the following without using chains of conditions: -// After... -if let PAT = ( EXPR && EXPR ) { .. } +```rust +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"); + } + } + } +} +``` -// Before... -if let PAT = EXPR || EXPR { .. } +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: -// After... -if let PAT = ( EXPR || EXPR ) { .. } +```rust +# let foo = Some(123); +# let condition1 = true; +# let condition2 = false; +// Parentheses are required here. +if let Some(x) = foo && (condition1 || condition2) { /*...*/ } ``` + [_BlockExpression_]: block-expr.md [_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 +[`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 308f3e346..d9458d56f 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -5,24 +5,21 @@ >    [_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: +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 @@ -40,12 +37,28 @@ A `loop` expression containing associated [`break` expression(s)](#break-express ## Predicate loops > **Syntax**\ -> _PredicateLoopExpression_ :\ ->    `while` [_Expression_]_except struct expression_ [_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. +> [_PredicateLoopExpression_] :\ +>    `while` _WhileConditions_ [_BlockExpression_] +> +> _WhileConditions_ :\ +>    _WhileCondition_ ( && _WhileCondition_ )* +> +> _WhileCondition_ :\ +>       [_Expression_]_except struct expression_\ +>    | `let` [_Pattern_] `=` [_Scrutinee_] + +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_]. + +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: @@ -58,17 +71,10 @@ while i < 10 { } ``` -## Predicate pattern loops - -> **Syntax**\ -> [_PredicatePatternLoopExpression_] :\ ->    `while` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ -> [_BlockExpression_] - +### `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]; @@ -115,7 +121,28 @@ 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 + +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: + +```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; + } +} +``` ## Iterator loops @@ -233,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. @@ -264,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