Skip to content

Commit

Permalink
Merge pull request #1251 from compiler-errors/revert-let-chains
Browse files Browse the repository at this point in the history
Revert let chains reference docs
  • Loading branch information
ehuss authored Aug 14, 2022
2 parents e1dc762 + db6fc4e commit 8d28c49
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 145 deletions.
2 changes: 1 addition & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/attributes/type_system.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/const_eval.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
4 changes: 2 additions & 2 deletions src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

[`<T as std::ops::Drop>::drop`]: ../std/ops/trait.Drop.html#tymethod.drop
Expand Down
6 changes: 4 additions & 2 deletions src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
> &nbsp;&nbsp; &nbsp;&nbsp; | [_UnsafeBlockExpression_]\
> &nbsp;&nbsp; &nbsp;&nbsp; | [_LoopExpression_]\
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IfExpression_]\
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IfLetExpression_]\
> &nbsp;&nbsp; &nbsp;&nbsp; | [_MatchExpression_]\
> &nbsp;&nbsp; )
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/expressions/block-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
156 changes: 81 additions & 75 deletions src/expressions/if-expr.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
# `if` expressions
# `if` and `if let` expressions

## Syntax
## `if` expressions

> **<sup>Syntax</sup>**\
> _IfExpression_ :\
> &nbsp;&nbsp; `if` _IfConditions_ [_BlockExpression_]\
> &nbsp;&nbsp; (`else` ( [_BlockExpression_] | _IfExpression_ ) )<sup>\?</sup>
>
> _IfConditions_ :\
> &nbsp;&nbsp; _IfCondition_ ( && _IfCondition_ )*
>
> _IfCondition_ :\
> &nbsp;&nbsp; &nbsp;&nbsp; [_Expression_]<sub>_except struct expression_</sub>\
> &nbsp;&nbsp; | `let` [_Pattern_] `=` [_Scrutinee_]
> &nbsp;&nbsp; `if` [_Expression_]<sub>_except struct expression_</sub> [_BlockExpression_]\
> &nbsp;&nbsp; (`else` (
> [_BlockExpression_]
> | _IfExpression_
> | _IfLetExpression_ ) )<sup>\?</sup>
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
Expand All @@ -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 {
Expand All @@ -47,23 +37,34 @@ 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:
> **<sup>Syntax</sup>**\
> _IfLetExpression_ :\
> &nbsp;&nbsp; `if` `let` [_Pattern_] `=` [_Scrutinee_]<sub>_except lazy boolean operator expression_</sub>
> [_BlockExpression_]\
> &nbsp;&nbsp; (`else` (
> [_BlockExpression_]
> | _IfExpression_
> | _IfLetExpression_ ) )<sup>\?</sup>
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 {
// This block is evaluated instead.
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);
}
Expand All @@ -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:

<!-- ignore: expansion example -->
```rust,ignore
if let PATS = EXPR {
/* body */
} else {
/*else */
}
```

is equivalent to

<!-- ignore: expansion example -->
```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 {
Expand All @@ -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:
<!-- ignore: psuedo code -->
```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
Loading

0 comments on commit 8d28c49

Please sign in to comment.