Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document destructuring assignment #1116

Merged
merged 4 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
- [Match expressions](expressions/match-expr.md)
- [Return expressions](expressions/return-expr.md)
- [Await expressions](expressions/await-expr.md)
- [Underscore expressions](expressions/underscore-expr.md)

- [Patterns](patterns.md)

Expand Down
28 changes: 22 additions & 6 deletions src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
>       | [_BreakExpression_]\
>       | [_RangeExpression_]\
>       | [_ReturnExpression_]\
>       | [_UnderscoreExpression_]\
>       | [_MacroInvocation_]\
>    )
>
Expand Down Expand Up @@ -139,10 +140,11 @@ assert_eq!(

## Place Expressions and Value Expressions

Expressions are divided into two main categories: place expressions and
value expressions. Likewise, within each expression, operands may occur
in either place context or value context. The evaluation of an expression
depends both on its own category and the context it occurs within.
Expressions are divided into two main categories: place expressions and value
expressions; there is also a third, minor category of expressions called
assignee expressions. Within each expression, operands may likewise occur in
either place context or value context. The evaluation of an expression depends
both on its own category and the context it occurs within.

A *place expression* is an expression that represents a memory location. These
expressions are [paths] which refer to local variables, [static variables],
Expand All @@ -154,8 +156,7 @@ A *value expression* is an expression that represents an actual value.

The following contexts are *place expression* contexts:

* The left operand of an [assignment][assign] or [compound assignment]
expression.
* The left operand of a [compound assignment] expression.
* The operand of a unary [borrow] or [dereference][deref] operator.
* The operand of a field expression.
* The indexed operand of an array indexing expression.
Expand All @@ -168,6 +169,20 @@ The following contexts are *place expression* contexts:
> Note: Historically, place expressions were called *lvalues* and value
> expressions were called *rvalues*.

An *assignee expression* is an expression that appears in the left operand of an
[assignment][assign] expression. Explicitly, the assignee expressions are:

- Place expressions.
- [Underscores][_UnderscoreExpression_].
- [Tuples][_TupleExpression_] of assignee expressions.
- [Slices][_ArrayExpression_] of assingee expressions.
- [Tuple structs][_StructExpression_] of assignee expressions.
- [Structs][_StructExpression_] of assignee expressions (with optionally named
fields).
- [Unit structs][_StructExpression_].

Arbitrary parenthesisation is permitted inside assignee expressions.

### Moved and copied types

When a place expression is evaluated in a value expression context, or is bound
Expand Down Expand Up @@ -349,4 +364,5 @@ They are never allowed before:
[_TupleExpression_]: expressions/tuple-expr.md
[_TupleIndexingExpression_]: expressions/tuple-expr.md#tuple-indexing-expressions
[_TypeCastExpression_]: expressions/operator-expr.md#type-cast-expressions
[_UnderscoreExpression_]: expressions/underscore-expr.md
[_UnsafeBlockExpression_]: expressions/block-expr.md#unsafe-blocks
67 changes: 63 additions & 4 deletions src/expressions/operator-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,15 @@ assert_eq!(values[1], 3);

An *assignment expression* moves a value into a specified place.

An assignment expression consists of a [mutable] [place expression], the *assigned place operand*, followed by an equals sign (`=`) and a [value expression], the *assigned value operand*.
An assignment expression consists of a [mutable] [assignee expression], the *assignee operand*, followed by an equals sign (`=`) and a [value expression], the *assigned value operand*.
In its most basic form, an assignee expression is a [place expression], and we discuss this case first.
The more general case of destructuring assignment is discussed below, but this case always decomposes into sequential assignments to place expressions, which may be considered the more fundamental case.

Unlike other place operands, the assigned place operand must be a place expression.
Attempting to use a value expression is a compiler error rather than promoting it to a temporary.
### Basic assignments

Evaluating assignment expressions begins by evaluating its operands.
The assigned value operand is evaluated first, followed by the assigned place operand.
The assigned value operand is evaluated first, followed by the assignee expression.
For destructuring assignment, subexpressions of the assignee expression are evaluated left-to-right.

> **Note**: This is different than other expressions in that the right operand is evaluated before the left one.

Expand All @@ -451,6 +453,60 @@ let y = 0;
x = y;
```

### Destructuring assignments

Destructuring assignment is a counterpart to destructuring pattern matches for variable declaration, permitting assignment to complex values, such as tuples or structs.
For instance, we may swap two mutable variables:

```rust
let (mut a, mut b) = (0, 1);
// Swap `a` and `b` using destructuring assignment.
(b, a) = (a, b);
```

In contrast to destructuring declarations using `let`, patterns may not appear on the left-hand side of an assignment due to syntactic ambiguities.
Instead, a group of expressions that correspond to patterns are designated to be [assignee expressions][assignee expression], and permitted on the left-hand side of an assignment.
Assignee expressions are then desugared to pattern matches followed by sequential assignment.
The desugared patterns must be irrefutable: in particular, this means that only slice patterns whose length is known at compile-time, and the trivial slice `[..]`, are permitted for destructuring assignment.

The desugaring method is straightforward, and is illustrated best by example.

```rust
# struct Struct { x: u32, y: u32 }
# let (mut a, mut b) = (0, 0);
(a, b) = (3, 4);

[a, b] = [3, 4];

Struct { x: a, y: b } = Struct { x: 3, y: 4};

// desugars to:

{
let (_a, _b) = (3, 4);
a = _a;
b = _b;
}

{
let [_a, _b] = [3, 4];
a = _a;
b = _b;
}

{
let Struct { x: _a, y: _b } = Struct { x: 3, y: 4};
a = _a;
b = _b;
}
```

Identifiers are not forbidden from being used multiple times in a single assignee expression.

[Underscore expressions][_UnderscoreExpression_] and empty [range expressions][_RangeExpression_] may be used to ignore certain values, without binding them.

Note that default binding modes do not apply for the desugared expression.

## Compound assignment expressions

> **<sup>Syntax</sup>**\
Expand Down Expand Up @@ -530,6 +586,7 @@ See [this test] for an example of using this dependency.
[logical xor]: ../types/boolean.md#logical-xor
[mutable]: ../expressions.md#mutability
[place expression]: ../expressions.md#place-expressions-and-value-expressions
[assignee expression]: ../expressions.md#place-expressions-and-value-expressions
[undefined behavior]: ../behavior-considered-undefined.md
[unit]: ../types/tuple.md
[value expression]: ../expressions.md#place-expressions-and-value-expressions
Expand All @@ -552,3 +609,5 @@ See [this test] for an example of using this dependency.

[_Expression_]: ../expressions.md
[_TypeNoBounds_]: ../types.md#type-expressions
[_RangeExpression_]: ./range-expr.md
[_UnderscoreExpression_]: ./underscore-expr.md
17 changes: 17 additions & 0 deletions src/expressions/underscore-expr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# `_` expressions

> **<sup>Syntax</sup>**\
> _UnderscoreExpression_ :\
> &nbsp;&nbsp; `_`

Underscore expressions, denoted with the symbol `_`, are used to signify a
placeholder in a destructuring assignment. They may only appear in the left-hand
side of an assignment.

An example of an `_` expression:

```rust
let p = (1, 2);
let mut a = 0;
(_, a) = p;
```
6 changes: 6 additions & 0 deletions src/macros-by-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ the syntax element that matched them. The keyword metavariable `$crate` can be
used to refer to the current crate; see [Hygiene] below. Metavariables can be
transcribed more than once or not at all.

For reasons of backwards compatibility, though `_` [is also an
expression][_UnderscoreExpression_], a standalone underscore is not matched by
the `expr` fragment specifier. However, `_` is matched by the `expr` fragment
specifier when it appears as a subexpression.

> **Edition Differences**: Starting with the 2021 edition, `pat` fragment-specifiers match top-level or-patterns (that is, they accept [_Pattern_]).
>
> Before the 2021 edition, they match exactly the same fragments as `pat_param` (that is, they accept [_PatternNoTopAlt_]).
Expand Down Expand Up @@ -506,6 +511,7 @@ For more detail, see the [formal specification].
[_Token_]: tokens.md
[_TypePath_]: paths.md#paths-in-types
[_Type_]: types.md#type-expressions
[_UnderscoreExpression_]: expressions/underscore-expr.md
[_Visibility_]: visibility-and-privacy.md
[formal specification]: macro-ambiguity.md
[token]: tokens.md
Expand Down
3 changes: 2 additions & 1 deletion src/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ usages and meanings are defined in the linked pages.
| `>=` | Ge | [Greater than or equal to][comparison], [Generics]
| `<=` | Le | [Less than or equal to][comparison]
| `@` | At | [Subpattern binding]
| `_` | Underscore | [Wildcard patterns], [Inferred types], Unnamed items in [constants], [extern crates], and [use declarations]
| `_` | Underscore | [Wildcard patterns], [Inferred types], Unnamed items in [constants], [extern crates], [use declarations], and [destructuring assignment]
| `.` | Dot | [Field access][field], [Tuple index]
| `..` | DotDot | [Range][range], [Struct expressions], [Patterns], [Range Patterns][rangepat]
| `...` | DotDotDot | [Variadic functions][extern], [Range patterns]
Expand Down Expand Up @@ -625,6 +625,7 @@ them are referred to as "token trees" in [macros]. The three types of brackets
[compound]: expressions/operator-expr.md#compound-assignment-expressions
[constants]: items/constant-items.md
[dereference]: expressions/operator-expr.md#the-dereference-operator
[destructuring assignment]: expressions/underscore-expr.md
[extern crates]: items/extern-crates.md
[extern]: items/external-blocks.md
[field]: expressions/field-expr.md
Expand Down