diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 6a8f72475..82d70d043 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 expressions](expressions/if-expr.md) + - [If and if let 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 d15836fb0..729069d26 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -133,6 +133,7 @@ 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 d735a20ec..c0560376c 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] and [while] expressions. -* [if] and [match] expressions. +* [loop], [while] and [`while let`] expressions. +* [if], [`if let`] and [match] expressions. ## Const context @@ -121,6 +121,7 @@ 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 @@ -137,3 +138,4 @@ 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 083ed86eb..6d616b3e7 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-patterns +[`if let`]: expressions/if-expr.md#if-let-expressions [`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#while-let-patterns +[`while let`]: expressions/loop-expr.md#predicate-pattern-loops [`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 5b6525fec..32ee658ff 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -38,6 +38,7 @@ >       | [_UnsafeBlockExpression_]\ >       | [_LoopExpression_]\ >       | [_IfExpression_]\ +>       | [_IfLetExpression_]\ >       | [_MatchExpression_]\ >    ) @@ -294,13 +295,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-patterns +[`if let`]: expressions/if-expr.md#if-let-expressions [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#while-let-patterns +[`while let`]: expressions/loop-expr.md#predicate-pattern-loops [array expressions]: expressions/array-expr.md [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions @@ -347,6 +348,7 @@ 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 01e6ef95c..a68b27e56 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`], and [`for`]). +* Loop bodies ([`loop`], [`while`], [`while let`], 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,6 +172,7 @@ 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 9de65df87..591437fc9 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -1,31 +1,22 @@ -# `if` expressions +# `if` and `if let` expressions -## Syntax +## `if` expressions > **Syntax**\ > _IfExpression_ :\ ->    `if` _IfConditions_ [_BlockExpression_]\ ->    (`else` ( [_BlockExpression_] | _IfExpression_ ) )\? -> -> _IfConditions_ :\ ->    _IfCondition_ ( && _IfCondition_ )* -> -> _IfCondition_ :\ ->       [_Expression_]_except struct expression_\ ->    | `let` [_Pattern_] `=` [_Scrutinee_] +>    `if` [_Expression_]_except struct expression_ [_BlockExpression_]\ +>    (`else` ( +> [_BlockExpression_] +> | _IfExpression_ +> | _IfLetExpression_ ) )\? An `if` expression is a conditional branch in program control. -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. +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. 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 @@ -38,7 +29,6 @@ if x == 4 { println!("x is something else"); } -// `if` can be used as an expression. let y = if 12 * 15 > 150 { "Bigger" } else { @@ -47,15 +37,26 @@ let y = if 12 * 15 > 150 { assert_eq!(y, "Bigger"); ``` -## `if let` patterns +## `if let` expressions -`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: +> **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. ```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 +64,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,8 +74,44 @@ if let _ = 5 { } ``` -Multiple patterns may be specified with the `|` operator. -This has the same semantics as with `|` in [`match` expressions]: +`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: ```rust enum E { @@ -88,62 +125,31 @@ if let E::X(n) | E::Y(n) = v { } ``` -## 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"); - } -} -``` +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 above is equivalent to the following without using chains of conditions: + +```rust,ignore +// Before... +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"); - } - } - } -} -``` +// After... +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: +// Before... +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) { /*...*/ } +// After... +if let PAT = ( EXPR || EXPR ) { .. } ``` - [_BlockExpression_]: block-expr.md [_Expression_]: ../expressions.md [_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators [_Pattern_]: ../patterns.md [_Scrutinee_]: match-expr.md -[`match` expressions]: 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 d9458d56f..308f3e346 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -5,21 +5,24 @@ >    [_LoopLabel_]? (\ >          [_InfiniteLoopExpression_]\ >       | [_PredicateLoopExpression_]\ +>       | [_PredicatePatternLoopExpression_]\ >       | [_IteratorLoopExpression_]\ >    ) [_LoopLabel_]: #loop-labels [_InfiniteLoopExpression_]: #infinite-loops [_PredicateLoopExpression_]: #predicate-loops +[_PredicatePatternLoopExpression_]: #predicate-pattern-loops [_IteratorLoopExpression_]: #iterator-loops -Rust supports three loop expressions: +Rust supports four 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 three types of loop support [`break` expressions](#break-expressions), [`continue` expressions](#continue-expressions), and [labels](#loop-labels). +All four 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,28 +40,12 @@ A `loop` expression containing associated [`break` expression(s)](#break-express ## Predicate loops > **Syntax**\ -> [_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 `()`. +> _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. An example: @@ -71,10 +58,17 @@ while i < 10 { } ``` -### `while let` patterns +## Predicate pattern loops + +> **Syntax**\ +> [_PredicatePatternLoopExpression_] :\ +>    `while` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ +> [_BlockExpression_] + -`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: +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. ```rust let mut x = vec![1, 2, 3]; @@ -121,28 +115,7 @@ while let Some(v @ 1) | Some(v @ 2) = vals.pop() { } ``` -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; - } -} -``` +As is the case in [`if let` expressions], the scrutinee cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. ## Iterator loops @@ -260,7 +233,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 operands controlling the loop. +In the case of a `while` loop, the head is the conditional expression 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. @@ -291,12 +264,11 @@ 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 type]: ../types/boolean.md +[boolean]: ../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 e0643fe2a..8caeff200 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-patterns +[if let]: if-expr.md#if-let-expressions [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 26bce0bd1..fd8f50cd0 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-patterns +[`if let`]: expressions/if-expr.md#if-let-expressions [`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#while-let-patterns +[`while let`]: expressions/loop-expr.md#predicate-pattern-loops [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 1c94745d8..14811697c 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-patterns +[`if let`]: ../expressions/if-expr.md#if-let-expressions [`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#while-let-patterns +[`while let`]: ../expressions/loop-expr.md#predicate-pattern-loops [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 3a1c609bf..5c81ecd87 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#while-let-patterns) +* [`while let` expressions](expressions/loop-expr.md#predicate-pattern-loops) * [`for` expressions](expressions/loop-expr.md#iterator-loops) ## Destructuring diff --git a/src/tokens.md b/src/tokens.md index 71a3527fd..197c20147 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-patterns +[if let]: expressions/if-expr.md#if-let-expressions [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#while-let-patterns +[while let]: expressions/loop-expr.md#predicate-pattern-loops