From 3abb0e381abb22a7f9bde26e749070159923a4cc Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 23 Feb 2025 18:19:49 +0100 Subject: [PATCH 1/5] Document let chains again --- src/SUMMARY.md | 2 +- src/attributes/type_system.md | 1 - src/const_eval.md | 8 +- src/destructors.md | 15 +-- src/expressions.md | 5 +- src/expressions/block-expr.md | 3 +- src/expressions/if-expr.md | 172 ++++++++++++++++----------------- src/expressions/loop-expr.md | 85 +++++++++++----- src/expressions/struct-expr.md | 2 +- src/names.md | 4 +- src/names/namespaces.md | 4 +- src/names/scopes.md | 10 +- src/patterns.md | 2 +- src/tokens.md | 4 +- 14 files changed, 173 insertions(+), 144 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 980a61ef0..cbee8c018 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -63,7 +63,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 a6f05d788..0a3d4f15f 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -206,7 +206,6 @@ let _ = EnumWithNonExhaustiveVariants::First as u8; Non-exhaustive types are always considered inhabited in downstream crates. -[`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 07f704512..8475ef962 100644 --- a/src/const_eval.md +++ b/src/const_eval.md @@ -102,10 +102,12 @@ r[const-eval.const-expr.const-fn] * Calls of [const functions] and const methods. r[const-eval.const-expr.loop] -* [loop], [while] and [`while let`] expressions. +* [loop] and [while] expressions. r[const-eval.const-expr.if-match] -* [if], [`if let`] and [match] expressions. +* [if] and [match] expressions. + +expressions.") r[const-eval.const-context] ## Const context @@ -185,7 +187,6 @@ of whether you are building on a `64` bit or a `32` bit system. [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 @@ -202,4 +203,3 @@ of whether you are building on a `64` bit or a `32` bit system. [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 2f4711815..dbf1dcd1d 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -67,8 +67,9 @@ leaves a drop scope all variables associated to that scope are dropped in reverse order of declaration (for variables) or creation (for temporaries). r[destructors.scope.desugaring] -Drop scopes are determined after replacing [`for`], [`if let`], and -[`while let`] expressions with the equivalent expressions using [`match`]. +Drop scopes can be determined by replacing [`for`], [`if`], and [`while`] +expressions with equivalent expressions using [`match`], [`loop`] and +`break`. r[destructors.scope.operators] Overloaded operators are not distinguished from built-in operators and [binding @@ -203,11 +204,11 @@ smallest scope that contains the expression and is one of the following: * A statement. * The body of an [`if`], [`while`] or [`loop`] expression. * The `else` block of an `if` expression. -* The condition expression of an `if` or `while` expression, or a `match` - guard. +* The non-pattern matching condition expression of an `if` or `while` expression, + or a `match` guard. * The body expression for a match arm. * Each operand of a [lazy boolean expression]. -* The pattern-matching condition and consequent body of [`if let`] ([destructors.scope.temporary.edition2024]). +* The pattern-matching condition(s) and consequent body of [`if`] ([destructors.scope.temporary.edition2024]). * The entirety of the tail expression of a block ([destructors.scope.temporary.edition2024]). > [!NOTE] @@ -479,10 +480,10 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi [tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-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 diff --git a/src/expressions.md b/src/expressions.md index b82f3b52e..eacf005e9 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -41,7 +41,6 @@ ExpressionWithBlock -> | UnsafeBlockExpression | LoopExpression | IfExpression - | IfLetExpression | MatchExpression ) ``` @@ -311,9 +310,9 @@ They are never allowed before: [`Copy`]: special-types-and-traits.md#copy [`Drop`]: special-types-and-traits.md#drop -[`if let`]: expressions/if-expr.md#if-let-expressions +[`if let`]: expressions/if-expr.md#if-let-patterns [`Sized`]: special-types-and-traits.md#sized -[`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 [assign]: expressions/operator-expr.md#assignment-expressions diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index 0a63e7272..da0f93b36 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -258,7 +258,7 @@ r[expr.block.attributes.inner-attributes] [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. @@ -282,7 +282,6 @@ fn is_unix_platform() -> bool { [`for`]: loop-expr.md#iterator-loops [`loop`]: loop-expr.md#infinite-loops [`unsafe` blocks]: ../unsafe-keyword.md#unsafe-blocks-unsafe- -[`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 dca3538d8..ca884a7ee 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -1,34 +1,40 @@ r[expr.if] -# `if` and `if let` expressions - -## `if` expressions +# `if` expressions r[expr.if.syntax] ```grammar,expressions IfExpression -> - `if` Expression _except [StructExpression]_ BlockExpression - (`else` ( BlockExpression | IfExpression | IfLetExpression ) )? + `if` IfConditions BlockExpression + (`else` ( BlockExpression | IfExpression ) )? + +IfConditions -> IfCondition ( `&&` IfCondition )* + +IfCondition -> + Expression _except [StructExpression]_ + | `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ ``` r[expr.if.intro] -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 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. -r[expr.if.condition-bool] -The condition operands must have the [boolean type]. +r[expr.if.condition] +Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match. r[expr.if.condition-true] -If a condition operand evaluates to `true`, the consequent block is executed and any subsequent `else if` or `else` block is skipped. +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. r[expr.if.else-if] -If a condition operand evaluates to `false`, the consequent block is skipped and any subsequent `else if` condition is evaluated. +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. r[expr.if.else] If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed. r[expr.if.result] -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. r[expr.if.type] An `if` expression must have the same type in all situations. @@ -43,6 +49,7 @@ if x == 4 { println!("x is something else"); } +// `if` can be used as an expression. let y = if 12 * 15 > 150 { "Bigger" } else { @@ -52,31 +59,17 @@ assert_eq!(y, "Bigger"); ``` r[expr.if.let] -## `if let` expressions - -r[expr.if.let.syntax] -```grammar,expressions -IfLetExpression -> - `if` `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ BlockExpression - (`else` ( BlockExpression | IfExpression | IfLetExpression ) )? -``` +## `if let` patterns r[expr.if.let.intro] -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. - -r[expr.if.let.pattern] -If the value of the scrutinee matches the pattern, the corresponding block will execute. +`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully. -r[expr.if.let.else] -Otherwise, flow proceeds to the following `else` block if it exists. - -r[expr.if.let.result] -Like `if` expressions, `if let` expressions have a value determined by the block that is evaluated. +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 { @@ -84,7 +77,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); } @@ -94,47 +87,9 @@ if let _ = 5 { } ``` -r[expr.if.let.else-if] -`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); -``` - -r[expr.if.let.desugaring] -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 -} -``` - r[expr.if.let.or-pattern] -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 { @@ -148,27 +103,70 @@ if let E::X(n) | E::Y(n) = v { } ``` -r[expr.if.let.lazy-bool] -The expression cannot be a [lazy boolean operator expression][expr.bool-logic]. -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: +r[expr.if.chains] +## Chains of conditions + +r[expr.if.chains.intro] +Multiple condition operands can be separated with `&&`. + +r[expr.if.chains.order] +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. + +r[expr.if.chains.bindings] +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,ignore -// Before... -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"); + } +} +``` -// After... -if let PAT = ( EXPR && EXPR ) { .. } +The above is equivalent to the following without using chains of conditions: -// 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"); + } + } + } +} +``` + +r[expr.if.chains.or] +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) { /*...*/ } ``` -[_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 +r[expr.if.edition2024] +> **Edition differences**: Before the 2024 edition, let chains are not supported and only a single _IfCondition_ is allowed in an `if` expression. + +[_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 [boolean type]: ../types/boolean.md [scrutinee]: ../glossary.md#scrutinee diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 310b3ad4d..3d06909e1 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -7,23 +7,21 @@ LoopExpression -> LoopLabel? ( InfiniteLoopExpression | PredicateLoopExpression - | PredicatePatternLoopExpression | IteratorLoopExpression | LabelBlockExpression ) ``` r[expr.loop.intro] -Rust supports five 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. * A [labelled block expression](#labelled-block-expressions) runs a loop exactly once, but allows exiting the loop early with `break`. r[expr.loop.break-label] -All five types of loop support [`break` expressions](#break-expressions), and [labels](#loop-labels). +All four types of loop support [`break` expressions](#break-expressions), and [labels](#loop-labels). r[expr.loop.continue-label] All except labelled block expressions support [`continue` expressions](#continue-expressions). @@ -52,18 +50,39 @@ A `loop` expression containing associated [`break` expression(s)](#break-express r[expr.loop.while] ## Predicate loops -r[expr.loop.while.syntax] +r[expr.loop.while.grammar] ```grammar,expressions -PredicateLoopExpression -> `while` Expression _except [StructExpression]_ BlockExpression +PredicateLoopExpression -> `while` WhileConditions BlockExpression + +WhileConditions -> WhileCondition ( `&&` WhileCondition )* + +WhileCondition -> + Expression _except [StructExpression]_ + | `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ ``` r[expr.loop.while.intro] -A `while` loop begins by evaluating the [boolean] loop conditional operand. +A `while` loop expression allows repeating the evaluation of a block while a set of conditions remain true. + +r[expr.loop.while.syntax] +The syntax of a `while` expression is a sequence of one or more condition operands separated by `&&`, +followed by a [_BlockExpression_]. r[expr.loop.while.condition] -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. + +r[expr.loop.while.repeat] +After the loop body successfully executes, the condition operands are re-evaluated to determine if the body should be executed again. + +r[expr.loop.while.exit] +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. + +r[expr.loop.while.eval] +A `while` expression evaluates to `()`. An example: @@ -77,20 +96,11 @@ while i < 10 { ``` r[expr.loop.while.let] -## Predicate pattern loops - -r[expr.loop.while.let.syntax] -```grammar,expressions -PredicatePatternLoopExpression -> - `while` `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ BlockExpression -``` +### `while let` patterns r[expr.loop.while.let.intro] -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. - -r[expr.loop.while.let.condition] -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]; @@ -139,8 +149,28 @@ while let Some(v @ 1) | Some(v @ 2) = vals.pop() { } ``` -r[expr.loop.while.let.lazy-bool] -As is the case in [`if let` expressions], the scrutinee cannot be a [lazy boolean operator expression][expr.bool-logic]. +r[expr.loop.while.chains] +### `while` condition chains + +r[expr.loop.while.chains.intro] +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; + } +} +``` r[expr.loop.for] ## Iterator loops @@ -333,7 +363,7 @@ r[expr.loop.continue.intro] When `continue` is encountered, the current iteration of the associated loop body is immediately terminated, returning control to the loop *head*. r[expr.loop.continue.while] -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. r[expr.loop.continue.for] In the case of a `for` loop, the head is the call-expression controlling the loop. @@ -369,8 +399,11 @@ r[expr.loop.break-value.loop] In the case a `loop` has an associated `break`, it is not considered diverging, and the `loop` must have a type compatible with each `break` expression. `break` without an expression is considered identical to `break` with expression `()`. +[_BlockExpression_]: block-expr.md +[_Expression_]: ../expressions.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 -[`if let` expressions]: if-expr.md#if-let-expressions diff --git a/src/expressions/struct-expr.md b/src/expressions/struct-expr.md index 3c3dd15d4..afb555dc4 100644 --- a/src/expressions/struct-expr.md +++ b/src/expressions/struct-expr.md @@ -146,7 +146,7 @@ Point3d { x, y: y_value, z }; ``` [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 92d2cd82c..b9af0e535 100644 --- a/src/names.md +++ b/src/names.md @@ -135,13 +135,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 afe48a2e5..feb0e9771 100644 --- a/src/names/namespaces.md +++ b/src/names/namespaces.md @@ -123,14 +123,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/names/scopes.md b/src/names/scopes.md index cfc5ffd30..0a7240512 100644 --- a/src/names/scopes.md +++ b/src/names/scopes.md @@ -49,9 +49,9 @@ r[names.scopes.pattern-bindings.parameter] r[names.scopes.pattern-bindings.closure] * [Closure parameter] bindings are within the closure body. r[names.scopes.pattern-bindings.loop] -* [`for`] and [`while let`] bindings are within the loop body. -r[names.scopes.pattern-bindings.if-let] -* [`if let`] bindings are within the consequent block. +* [`for`] bindings are within the loop body. +r[names.scopes.pattern-bindings.let-chains] +* [`if let`] and [`while let`] bindings are valid in the following conditions as well as the consequent block. r[names.scopes.pattern-bindings.match-arm] * [`match` arms] bindings are within the [match guard] and the match arm expression. @@ -340,14 +340,14 @@ impl ImplExample { [`derive` attribute]: ../attributes/derive.md [`for` loop]: ../expressions/loop-expr.md#iterator-loops [`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 +[`while let`]: ../expressions/loop-expr.md#while-let-patterns [`let` statement]: ../statements.md#let-statements [`macro_export`]: ../macros-by-example.md#path-based-scope [`macro_use` prelude]: preludes.md#macro_use-prelude [`macro_use`]: ../macros-by-example.md#the-macro_use-attribute [`match` arms]: ../expressions/match-expr.md [`Self`]: ../paths.md#self-1 -[`while let`]: ../expressions/loop-expr.md#predicate-pattern-loops [Associated consts]: ../items/associated-items.md#associated-constants [associated items]: ../items/associated-items.md [Asterisk glob imports]: ../items/use-declarations.md diff --git a/src/patterns.md b/src/patterns.md index cda82076e..2d9ddb5ac 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -79,7 +79,7 @@ r[patterns.if-let] * [`if let` expressions](expressions/if-expr.md) r[patterns.while-let] -* [`while let` expressions](expressions/loop-expr.md#predicate-pattern-loops) +* [`while let` expressions](expressions/loop-expr.md#while-let-patterns) r[patterns.for] * [`for` expressions](expressions/loop-expr.md#iterator-loops) diff --git a/src/tokens.md b/src/tokens.md index 7dffe9f51..294753e69 100644 --- a/src/tokens.md +++ b/src/tokens.md @@ -1049,7 +1049,7 @@ r[lex.token.reserved-guards.edition2024] [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 [Integer literal expressions]: expressions/literal-expr.md#integer-literal-expressions [keywords]: keywords.md [lazy-bool]: expressions/operator-expr.md#lazy-boolean-operators @@ -1080,4 +1080,4 @@ r[lex.token.reserved-guards.edition2024] [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 From 872ab781485f3ccd508a3f699c5dea26a7ed8fc8 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 25 Apr 2025 00:18:04 +0000 Subject: [PATCH 2/5] Use edition block syntax Let's switch the let chains update to use our the markdown syntax for edition differences. --- src/expressions/if-expr.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index ca884a7ee..80b7daf36 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -160,7 +160,8 @@ if let Some(x) = foo && (condition1 || condition2) { /*...*/ } ``` r[expr.if.edition2024] -> **Edition differences**: Before the 2024 edition, let chains are not supported and only a single _IfCondition_ is allowed in an `if` expression. +> [!EDITION-2024] +> Before the 2024 edition, let chains are not supported and only a single _IfCondition_ is allowed in an `if` expression. [_BlockExpression_]: block-expr.md [_Expression_]: ../expressions.md From 7f7e7da39de907308993760b64a2594e7fe65923 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 25 Apr 2025 00:30:53 +0000 Subject: [PATCH 3/5] Remove stray text --- src/const_eval.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/const_eval.md b/src/const_eval.md index 8475ef962..33d562e22 100644 --- a/src/const_eval.md +++ b/src/const_eval.md @@ -107,8 +107,6 @@ r[const-eval.const-expr.loop] r[const-eval.const-expr.if-match] * [if] and [match] expressions. -expressions.") - r[const-eval.const-context] ## Const context [const context]: #const-context From 602430a1e03d0b5245027e1f304a8f3326e042b4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 3 May 2025 14:17:36 -0700 Subject: [PATCH 4/5] Update let chain grammar for minimum precedence This adds some more exclusions to the let chain grammar to capture the minimum precedence while parsing chains. Note that this is incomplete, see https://github.com/rust-lang/reference/issues/1811. I didn't want to block things on finalizing this. I also reworked the `while` grammar to just reuse the grammar from `if` to avoid the duplication. --- src/expressions/if-expr.md | 29 ++++++++++++++++++++++------- src/expressions/loop-expr.md | 9 +-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index 80b7daf36..4f8560937 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -4,16 +4,31 @@ r[expr.if] r[expr.if.syntax] ```grammar,expressions IfExpression -> - `if` IfConditions BlockExpression + `if` Conditions BlockExpression (`else` ( BlockExpression | IfExpression ) )? -IfConditions -> IfCondition ( `&&` IfCondition )* - -IfCondition -> +Conditions -> Expression _except [StructExpression]_ - | `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ + | LetChain + +LetChain -> LetChainCondition ( `&&` LetChainCondition )* + +LetChainCondition -> + Expression _except [ExcludedConditions]_ + | OuterAttribute* `let` Pattern `=` Scrutinee _except [ExcludedConditions]_ + +@root ExcludedConditions -> + StructExpression + | LazyBooleanExpression + | RangeExpr + | RangeFromExpr + | RangeInclusiveExpr + | AssignmentExpression + | CompoundAssignmentExpression ``` - + r[expr.if.intro] The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`, @@ -161,7 +176,7 @@ if let Some(x) = foo && (condition1 || condition2) { /*...*/ } r[expr.if.edition2024] > [!EDITION-2024] -> Before the 2024 edition, let chains are not supported and only a single _IfCondition_ is allowed in an `if` expression. +> Before the 2024 edition, let chains are not supported. That is, the [LetChain] grammar is not allowed in an `if` expression. [_BlockExpression_]: block-expr.md [_Expression_]: ../expressions.md diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 3d06909e1..0a237176e 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -52,15 +52,8 @@ r[expr.loop.while] r[expr.loop.while.grammar] ```grammar,expressions -PredicateLoopExpression -> `while` WhileConditions BlockExpression - -WhileConditions -> WhileCondition ( `&&` WhileCondition )* - -WhileCondition -> - Expression _except [StructExpression]_ - | `let` Pattern `=` Scrutinee _except [LazyBooleanExpression]_ +PredicateLoopExpression -> `while` Conditions BlockExpression ``` - r[expr.loop.while.intro] A `while` loop expression allows repeating the evaluation of a block while a set of conditions remain true. From 8b5822b99b6a996e98a927f6da4283a0ddc48bab Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 3 May 2025 14:26:54 -0700 Subject: [PATCH 5/5] Add some redirects for sections that were renamed This adds some redirects for header fragments that have changed to help external links go to the correct location. --- src/expressions/if-expr.md | 14 ++++++++++++++ src/expressions/loop-expr.md | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index 4f8560937..37e7d707c 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -186,3 +186,17 @@ r[expr.if.edition2024] [`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 0a237176e..ab15da5e2 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -400,3 +400,17 @@ In the case a `loop` has an associated `break`, it is not considered diverging, [boolean type]: ../types/boolean.md [scrutinee]: ../glossary.md#scrutinee [temporary values]: ../expressions.md#temporaries + +