From a065d3e4c2955b83a695ab14c8be05a54e1518e6 Mon Sep 17 00:00:00 2001 From: "Havvy (Ryan Scheel)" Date: Sun, 14 Feb 2021 11:53:38 -0800 Subject: [PATCH 1/4] Editorial cleanup on expressions (part 1) * Use "the syntax of" uniformly. * Use asterisks for all defined terms. * Define more terms, espcially in the syntax section. ** Reword things so that definitions are generally first. ** I did not necessarily go with the best wording; the idea is to improve, not perfect. I still need to dedicate time to each expressions individually. * Remove usage of "the compiler" and "Rust". ** This also involved rewording. ** How to deal with closure types and closure expressions is gonna be an interesting question to solve. I avoided solving it here. * Remove non-normative information or put them in a `Note`. * A few added section headers * Move links to the bottom Note that I've left quite a few nonsensical statements alone, such as any that use "denote". They'll be treated separately. About halfway through the list of expressions and this PR is getting large. So I'm gonna cut this one here, stopping at grouped expressions in the alphabetical list. --- src/expressions.md | 2 +- src/expressions/array-expr.md | 16 +++++--- src/expressions/await-expr.md | 30 +++++++------- src/expressions/block-expr.md | 69 +++++++++++++++++---------------- src/expressions/call-expr.md | 15 +++---- src/expressions/closure-expr.md | 38 ++++++++++-------- src/expressions/field-expr.md | 45 ++++++++++++++++----- src/expressions/grouped-expr.md | 8 +++- 8 files changed, 130 insertions(+), 93 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 09b433365..5646e4691 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -137,7 +137,7 @@ assert_eq!( ## Place Expressions and Value Expressions Expressions are divided into two main categories: place expressions and -value expressions. Likewise within each expression, sub-expressions may occur +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. diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index 2659b229e..7369d5378 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -10,15 +10,19 @@ >       [_Expression_] ( `,` [_Expression_] )\* `,`?\ >    | [_Expression_] `;` [_Expression_] -An _[array] expression_ can be written by enclosing zero or more comma-separated expressions of uniform type in square brackets. +*Array expressions* construct [arrays][array]. +Array expressions come in two forms. + +The first form lists out every value in the array. +The syntax for this form is a comma-separated list of operands of uniform type enclosed in square brackets. This produces an array containing each of these values in the order they are written. -Alternatively there can be exactly two expressions inside the brackets, separated by a semicolon. -The expression after the `;` must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. +The syntax for the second form is two operands separated by a semicolon (`;`) enclosed in square brackets. +The operand after the `;` must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. `[a; b]` creates an array containing `b` copies of the value of `a`. -If the expression after the semicolon has a value greater than 1 then this requires that the type of `a` is [`Copy`], or `a` must be a path to a constant item. +If the operand after the semicolon has a value greater than 1 then this requires that the type of `a` is [`Copy`], or `a` must be a path to a constant item. -When the repeat expression `a` is a constant item, it is evaluated `b` times. +When the repeat expression, `a`, is a constant item, it is evaluated `b` times. If `b` is 0, the constant item is not evaluated at all. For expressions that are not a constant item, it is evaluated exactly once, and then the result is copied `b` times. @@ -49,7 +53,7 @@ const EMPTY: Vec = Vec::new(); > _IndexExpression_ :\ >    [_Expression_] `[` [_Expression_] `]` -[Array] and [slice]-typed expressions can be indexed by writing a square-bracket-enclosed expression of type `usize` (the index) after them. +[Array] and [slice]-typed values can be indexed by writing a square-bracket-enclosed expression of type `usize` (the index) after them. When the array is mutable, the resulting [memory location] can be assigned to. For other types an index expression `a[b]` is equivalent to `*std::ops::Index::index(&a, b)`, or `*std::ops::IndexMut::index_mut(&mut a, b)` in a mutable place expression context. diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md index 509a70e83..dacfa83e2 100644 --- a/src/expressions/await-expr.md +++ b/src/expressions/await-expr.md @@ -4,9 +4,9 @@ > _AwaitExpression_ :\ >    [_Expression_] `.` `await` +*Await expressions* suspend the current computation until the given future is ready to produce a value. +The syntax for an await expression is an operand with a type that implements the Future trait, `.`, and then the `await` keyword. Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block]. -They operate on a [future]. -Their effect is to suspend the current computation until the given future is ready to produce a value. More specifically, an `.await` expression has the following effect. @@ -16,16 +16,6 @@ More specifically, an `.await` expression has the following effect. 3. If the call to `poll` returns [`Poll::Pending`], then the future returns `Poll::Pending`, suspending its state so that, when the surrounding async context is re-polled,execution returns to step 2; 4. Otherwise the call to `poll` must have returned [`Poll::Ready`], in which case the value contained in the [`Poll::Ready`] variant is used as the result of the `await` expression itself. -[`async fn`]: ../items/functions.md#async-functions -[`async` block]: block-expr.md#async-blocks -[future]: ../../std/future/trait.Future.html -[_Expression_]: ../expressions.md -[`Future::poll`]: ../../std/future/trait.Future.html#tymethod.poll -[`Context`]: ../../std/task/struct.Context.html -[`Pin::new_unchecked`]: ../../std/pin/struct.Pin.html#method.new_unchecked -[`Poll::Pending`]: ../../std/task/enum.Poll.html#variant.Pending -[`Poll::Ready`]: ../../std/task/enum.Poll.html#variant.Ready - > **Edition differences**: Await expressions are only available beginning with Rust 2018. ## Task context @@ -33,12 +23,9 @@ More specifically, an `.await` expression has the following effect. The task context refers to the [`Context`] which was supplied to the current [async context] when the async context itself was polled. Because `await` expressions are only legal in an async context, there must be some task context available. -[`Context`]: ../../std/task/struct.Context.html -[async context]: ../expressions/block-expr.md#async-context - ## Approximate desugaring -Effectively, an `.await` expression is roughly equivalent to the following (this desugaring is not normative): +Effectively, an `.await` expression is roughly equivalent to the following non-normative desugaring: ```rust,ignore @@ -55,3 +42,14 @@ match /* */ { where the `yield` pseudo-code returns `Poll::Pending` and, when re-invoked, resumes execution from that point. The variable `current_context` refers to the context taken from the async environment. + +[_Expression_]: ../expressions.md +[`async fn`]: ../items/functions.md#async-functions +[`async` block]: block-expr.md#async-blocks +[`context`]: ../../std/task/struct.Context.html +[`future::poll`]: ../../std/future/trait.Future.html#tymethod.poll +[`pin::new_unchecked`]: ../../std/pin/struct.Pin.html#method.new_unchecked +[`poll::Pending`]: ../../std/task/enum.Poll.html#variant.Pending +[`poll::Ready`]: ../../std/task/enum.Poll.html#variant.Ready +[async context]: ../expressions/block-expr.md#async-context +[future]: ../../std/future/trait.Future.html diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index 6d418e745..1cfad4874 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -16,10 +16,12 @@ A *block expression*, or *block*, is a control flow expression and anonymous nam As a control flow expression, a block sequentially executes its component non-item declaration statements and then its final optional expression. As an anonymous namespace scope, item declarations are only in scope inside the block itself and variables declared by `let` statements are in scope from the next statement until the end of the block. -Blocks are written as `{`, then any [inner attributes], then [statements], then an optional expression, and finally a `}`. -Statements are usually required to be followed by a semicolon, with two exceptions. -Item declaration statements do not need to be followed by a semicolon. -Expression statements usually require a following semicolon except if its outer expression is a flow control expression. +The syntax for a block is `{`, then any [inner attributes], then [statements], then an optional operand, and finally a `}`. + +Statements are usually required to be followed by a semicolon, with two exceptions: + +1. Item declaration statements do not need to be followed by a semicolon. +2. Expression statements usually require a following semicolon except if its outer expression is a flow control expression. Furthermore, extra semicolons between statements are allowed, but these semicolons do not affect semantics. When evaluating a block expression, each statement, except for item declaration statements, is executed sequentially. @@ -43,28 +45,30 @@ assert_eq!(5, five); > Note: As a control flow expression, if a block expression is the outer expression of an expression statement, the expected type is `()` unless it is followed immediately by a semicolon. -Blocks are always [value expressions] and evaluate the last expression in value expression context. -This can be used to force moving a value if really needed. -For example, the following example fails on the call to `consume_self` because the struct was moved out of `s` in the block expression. +Blocks are always [value expressions] and evaluate the last operand in value expression context. -```rust,compile_fail -struct Struct; - -impl Struct { - fn consume_self(self) {} - fn borrow_self(&self) {} -} -fn move_by_block_expression() { - let s = Struct; - - // Move the value out of `s` in the block expression. - (&{ s }).borrow_self(); - - // Fails to execute because `s` is moved out of. - s.consume_self(); -} -``` +> **Note**: This can be used to force moving a value if really needed. +> For example, the following example fails on the call to `consume_self` because the struct was moved out of `s` in the block expression. +> +> ```rust,compile_fail +> struct Struct; +> +> impl Struct { +> fn consume_self(self) {} +> fn borrow_self(&self) {} +> } +> +> fn move_by_block_expression() { +> let s = Struct; +> +> // Move the value out of `s` in the block expression. +> (&{ s }).borrow_self(); +> +> // Fails to execute because `s` is moved out of. +> s.consume_self(); +> } +> ``` ## `async` blocks @@ -72,7 +76,7 @@ fn move_by_block_expression() { > _AsyncBlockExpression_ :\ >    `async` `move`? _BlockExpression_ -An *async block* is a variant of a block expression which evaluates to a *future*. +An *async block* is a variant of a block expression which evaluates to a future. The final expression of the block, if present, determines the result value of the future. Executing an async block is similar to executing a closure expression: @@ -84,26 +88,17 @@ The actual data format for this type is unspecified. > **Edition differences**: Async blocks are only available beginning with Rust 2018. -[`std::ops::Fn`]: ../../std/ops/trait.Fn.html -[`std::future::Future`]: ../../std/future/trait.Future.html - ### Capture modes Async blocks capture variables from their environment using the same [capture modes] as closures. Like closures, when written `async { .. }` the capture mode for each variable will be inferred from the content of the block. `async move { .. }` blocks however will move all referenced variables into the resulting future. -[capture modes]: ../types/closure.md#capture-modes -[shared references]: ../types/pointer.md#shared-references- -[mutable reference]: ../types/pointer.md#mutables-references- - ### Async context Because async blocks construct a future, they define an **async context** which can in turn contain [`await` expressions]. Async contexts are established by async blocks as well as the bodies of async functions, whose semantics are defined in terms of async blocks. -[`await` expressions]: await-expr.md - ### Control-flow operators Async blocks act like a function boundary, much like closures. @@ -171,16 +166,22 @@ fn is_unix_platform() -> bool { [_ExpressionWithoutBlock_]: ../expressions.md [_InnerAttribute_]: ../attributes.md [_Statement_]: ../statements.md +[`await` expressions]: await-expr.md [`cfg`]: ../conditional-compilation.md [`for`]: loop-expr.md#iterator-loops [`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 +[capture modes]: ../types/closure.md#capture-modes [function]: ../items/functions.md [inner attributes]: ../attributes.md [method]: ../items/associated-items.md#methods +[mutable reference]: ../types/pointer.md#mutables-references- +[shared references]: ../types/pointer.md#shared-references- [statement]: ../statements.md [statements]: ../statements.md [struct]: struct-expr.md diff --git a/src/expressions/call-expr.md b/src/expressions/call-expr.md index 0992f5e9f..f657b0270 100644 --- a/src/expressions/call-expr.md +++ b/src/expressions/call-expr.md @@ -7,12 +7,13 @@ > _CallParams_ :\ >    [_Expression_] ( `,` [_Expression_] )\* `,`? -A _call expression_ consists of an expression followed by a parenthesized expression-list. -It invokes a function, providing zero or more input variables. +A *call expression* calls a function. +The syntax of a call expression is an operand, the *function operand*, followed by a parenthesized comma-separated list of operands, the *argument operands*. If the function eventually returns, then the expression completes. For [non-function types](../types/function-item.md), the expression f(...) uses the method on one of the [`std::ops::Fn`], [`std::ops::FnMut`] or [`std::ops::FnOnce`] traits, which differ in whether they take the type by reference, mutable reference, or take ownership respectively. An automatic borrow will be taken if needed. -Rust will also automatically dereference `f` as required. +`f` will also be automatically dereferences as required. + Some examples of call expressions: ```rust @@ -23,13 +24,13 @@ let name: &'static str = (|| "Rust")(); ## Disambiguating Function Calls -Rust treats all function calls as sugar for a more explicit, [fully-qualified syntax]. +All function calls are sugar for a more explicit [fully-qualified syntax]. Upon compilation, Rust will desugar all function calls into the explicit form. Rust may sometimes require you to qualify function calls with trait, depending on the ambiguity of a call in light of in-scope items. -> **Note**: In the past, the Rust community used the terms "Unambiguous Function Call Syntax", "Universal Function Call Syntax", or "UFCS", in documentation, issues, RFCs, and other community writings. -> However, the term lacks descriptive power and potentially confuses the issue at hand. -> We mention it here for searchability's sake. +> **Note**: In the past, the terms "Unambiguous Function Call Syntax", "Universal Function Call Syntax", or "UFCS", have been used in documentation, issues, RFCs, and other community writings. +> However, these terms lack descriptive power and potentially confuse the issue at hand. +> We mention them here for searchability's sake. Several situations often occur which result in ambiguities about the receiver or referent of method or associated function calls. These situations may include: diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 366984611..1b9d9df21 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -12,27 +12,28 @@ > _ClosureParam_ :\ >    [_OuterAttribute_]\* [_Pattern_] ( `:` [_Type_] )? -A _closure expression_, also know as a lambda expression or a lambda, defines a closure and denotes it as a value, in a single expression. -A closure expression is a pipe-symbol-delimited (`|`) list of irrefutable [patterns] followed by an expression. -Type annotations may optionally be added for the type of the parameters or for the return type. -If there is a return type, the expression used for the body of the closure must be a normal [block]. -A closure expression also may begin with the `move` keyword before the initial `|`. +A *closure expression*, also know as a lambda expression or a lambda, defines a [closure type] and evaluates to a value of that type. +The syntax for a closure expression is an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, the *return type*, and then an operand, the *closure body*. +The optional type after each pattern is a type annotation for the pattern. +If there is a return type, the closure body must be a [block]. A closure expression denotes a function that maps a list of parameters onto the expression that follows the parameters. -Just like a [`let` binding], the parameters are irrefutable [patterns], whose type annotation is optional and will be inferred from context if not given. +Just like a [`let` binding], the closure parameters are irrefutable [patterns], whose type annotation is optional and will be inferred from context if not given. Each closure expression has a unique, anonymous type. -Closure expressions are most useful when passing functions as arguments to other functions, as an abbreviation for defining and capturing a separate function. - Significantly, closure expressions _capture their environment_, which regular [function definitions] do not. Without the `move` keyword, the closure expression [infers how it captures each variable from its environment](../types/closure.md#capture-modes), preferring to capture by shared reference, effectively borrowing all outer variables mentioned inside the closure's body. If needed the compiler will infer that instead mutable references should be taken, or that the values should be moved or copied (depending on their type) from the environment. A closure can be forced to capture its environment by copying or moving values by prefixing it with the `move` keyword. -This is often used to ensure that the closure's type is `'static`. +This is often used to ensure that the closure's lifetime is `'static`. + +## Closure trait implementations + +Which traits the closure type implement depends on how variables are captured and the types of the captured variables. +See the [call traits and coercions] chapter for how and when a closure implements `Fn`, `FnMut`, and `FnOnce`. +The closure type implements [`Send`] and [`Sync`] if the type of every captured variable also implements the trait. -The compiler will determine which of the [closure traits](../types/closure.md#call-traits-and-coercions) the closure's type will implement by how it acts on its captured variables. -The closure will also implement [`Send`](../special-types-and-traits.md#send) and/or [`Sync`](../special-types-and-traits.md#sync) if all of its captured types do. -These traits allow functions to accept closures using generics, even though the exact types can't be named. +## Example In this example, we define a function `ten_times` that takes a higher-order function argument, and we then call it with a closure expression as an argument, followed by a closure expression that moves values from its environment. @@ -55,15 +56,18 @@ ten_times(move |j| println!("{}, {}", word, j)); Attributes on closure parameters follow the same rules and restrictions as [regular function parameters]. -[block]: block-expr.md -[function definitions]: ../items/functions.md -[patterns]: ../patterns.md -[regular function parameters]: ../items/functions.md#attributes-on-function-parameters - [_Expression_]: ../expressions.md [_BlockExpression_]: block-expr.md [_TypeNoBounds_]: ../types.md#type-expressions [_Pattern_]: ../patterns.md [_Type_]: ../types.md#type-expressions [`let` binding]: ../statements.md#let-statements +[`Send`]: ../special-types-and-traits.md#send +[`Sync`]: ../special-types-and-traits.md#sync [_OuterAttribute_]: ../attributes.md +[block]: block-expr.md +[call traits and coercions]: ../types/closure.md#call-traits-and-coercions +[closure type]: ../types/closure.md +[function definitions]: ../items/functions.md +[patterns]: ../patterns.md +[regular function parameters]: ../items/functions.md#attributes-on-function-parameters diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index 59451d2b6..e0e52bc9d 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -4,8 +4,28 @@ > _FieldExpression_ :\ >    [_Expression_] `.` [IDENTIFIER] -A _field expression_ consists of an expression followed by a single dot and an [identifier], when not immediately followed by a parenthesized expression-list (the latter is always a [method call expression]). +A *field expression* is a [place expression] that evaluates to the location of a field of a type. +When the operand is [mutable], the field expression is also mutable. + +The syntax for a field expression is an operand, then a `.`, and finally an [identifier]. +Field expressions cannot be followed by a parenthetical comma-separated list of expressions, as that is parsed as a [method call expression]. +That is, they cannot be the function operand of a [call expression]. + +> **Note**: Wrap the field expression in a [parenthesized expression] to use it in a call expression. +> +> ```rust +> # struct HoldsCallable { callable: F } +> let holds_callable = HoldsCallable { callable: || () }; +> +> // Invalid: Parsed as calling the method "callable" +> // holds_callable.callable(); +> +> // Valid +> (holds_callable.callable)(); +> ``` + A field expression denotes a field of a [struct] or [union]. + To call a function stored in a struct, parentheses are needed around the field expression. @@ -13,19 +33,19 @@ To call a function stored in a struct, parentheses are needed around the field e mystruct.myfield; foo().x; (Struct {a: 10, b: 20}).a; -mystruct.method(); // Method expression (mystruct.function_field)() // Call expression containing a field expression ``` -A field access is a [place expression] referring to the location of that field. -When the subexpression is [mutable], the field expression is also mutable. +## Automatic dereferencing + +Also, if the type of the operand implements [`Deref`] or [`DerefMut`][`Deref`] depending on whether the operand is [mutable], it is *automatically dereferenced* as many times as necessary to make the field access possible. +This processes is also called *autoderef* for short. -Also, if the type of the expression to the left of the dot is a pointer, it is automatically dereferenced as many times as necessary to make the field access possible. -In cases of ambiguity, we prefer fewer autoderefs to more. +## Borrowing Finally, the fields of a struct or a reference to a struct are treated as separate entities when borrowing. -If the struct does not implement [`Drop`](../special-types-and-traits.md#drop) and is stored in a local variable, this also applies to moving out of each of its fields. -This also does not apply if automatic dereferencing is done though user defined types other than [`Box`](../special-types-and-traits.html#boxt). +If the struct does not implement [`Drop`] and is stored in a local variable, this also applies to moving out of each of its fields. +This also does not apply if automatic dereferencing is done though user-defined types other than [`Box`]. ```rust struct A { f1: String, f2: String, f3: String } @@ -42,9 +62,14 @@ let d: String = x.f3; // Move out of x.f3 ``` [_Expression_]: ../expressions.md +[`Box`]: ../special-types-and-traits.html#boxt +[`Deref`]: ../special-types-and-traits.html#deref-and-derefmut +[`drop`]: ../special-types-and-traits.md#drop [IDENTIFIER]: ../identifiers.md +[call expression]: call-expr.md [method call expression]: method-call-expr.md +[mutable]: ../expressions.md#mutability +[parenthesized expression]: grouped-expr.md +[place expression]: ../expressions.md#place-expressions-and-value-expressions [struct]: ../items/structs.md [union]: ../items/unions.md -[place expression]: ../expressions.md#place-expressions-and-value-expressions -[mutable]: ../expressions.md#mutability diff --git a/src/expressions/grouped-expr.md b/src/expressions/grouped-expr.md index 096b36c82..2911107b4 100644 --- a/src/expressions/grouped-expr.md +++ b/src/expressions/grouped-expr.md @@ -4,8 +4,12 @@ > _GroupedExpression_ :\ >    `(` [_InnerAttribute_]\* [_Expression_] `)` -An expression enclosed in parentheses evaluates to the result of the enclosed expression. -Parentheses can be used to explicitly specify evaluation order within an expression. +A *parenthesized expression* wrap a single expression, evaluating to that expression. +The syntax for a parenthesized expression is a `(`, then an operand, the *enclosed operand*, and then a `)`. + +An expression enclosed in parentheses evaluates to the value of the enclosed operand. + +Parentheses can be used to explicitly modify the precedence order of subexpressions within an expression. An example of a parenthesized expression: From 3da65a1602f575136fcc33d42a40a374eb4e2067 Mon Sep 17 00:00:00 2001 From: Ryan Scheel Date: Sat, 27 Feb 2021 23:02:47 -0800 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Eric Huss --- src/expressions/call-expr.md | 3 +-- src/expressions/field-expr.md | 10 +++++----- src/expressions/grouped-expr.md | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/expressions/call-expr.md b/src/expressions/call-expr.md index f657b0270..c9b590114 100644 --- a/src/expressions/call-expr.md +++ b/src/expressions/call-expr.md @@ -25,8 +25,7 @@ let name: &'static str = (|| "Rust")(); ## Disambiguating Function Calls All function calls are sugar for a more explicit [fully-qualified syntax]. -Upon compilation, Rust will desugar all function calls into the explicit form. -Rust may sometimes require you to qualify function calls with trait, depending on the ambiguity of a call in light of in-scope items. +Function calls may need to be fully qualified, depending on the ambiguity of a call in light of in-scope items. > **Note**: In the past, the terms "Unambiguous Function Call Syntax", "Universal Function Call Syntax", or "UFCS", have been used in documentation, issues, RFCs, and other community writings. > However, these terms lack descriptive power and potentially confuse the issue at hand. diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index e0e52bc9d..bddcf0c64 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -4,7 +4,7 @@ > _FieldExpression_ :\ >    [_Expression_] `.` [IDENTIFIER] -A *field expression* is a [place expression] that evaluates to the location of a field of a type. +A *field expression* is a [place expression] that evaluates to the location of a field of a [struct] or [union]. When the operand is [mutable], the field expression is also mutable. The syntax for a field expression is an operand, then a `.`, and finally an [identifier]. @@ -38,12 +38,12 @@ foo().x; ## Automatic dereferencing -Also, if the type of the operand implements [`Deref`] or [`DerefMut`][`Deref`] depending on whether the operand is [mutable], it is *automatically dereferenced* as many times as necessary to make the field access possible. +If the type of the operand implements [`Deref`] or [`DerefMut`][`Deref`] depending on whether the operand is [mutable], it is *automatically dereferenced* as many times as necessary to make the field access possible. This processes is also called *autoderef* for short. ## Borrowing -Finally, the fields of a struct or a reference to a struct are treated as separate entities when borrowing. +The fields of a struct or a reference to a struct are treated as separate entities when borrowing. If the struct does not implement [`Drop`] and is stored in a local variable, this also applies to moving out of each of its fields. This also does not apply if automatic dereferencing is done though user-defined types other than [`Box`]. @@ -62,8 +62,8 @@ let d: String = x.f3; // Move out of x.f3 ``` [_Expression_]: ../expressions.md -[`Box`]: ../special-types-and-traits.html#boxt -[`Deref`]: ../special-types-and-traits.html#deref-and-derefmut +[`Box`]: ../special-types-and-traits.md#boxt +[`Deref`]: ../special-types-and-traits.md#deref-and-derefmut [`drop`]: ../special-types-and-traits.md#drop [IDENTIFIER]: ../identifiers.md [call expression]: call-expr.md diff --git a/src/expressions/grouped-expr.md b/src/expressions/grouped-expr.md index 2911107b4..0b4197d18 100644 --- a/src/expressions/grouped-expr.md +++ b/src/expressions/grouped-expr.md @@ -4,7 +4,7 @@ > _GroupedExpression_ :\ >    `(` [_InnerAttribute_]\* [_Expression_] `)` -A *parenthesized expression* wrap a single expression, evaluating to that expression. +A *parenthesized expression* wraps a single expression, evaluating to that expression. The syntax for a parenthesized expression is a `(`, then an operand, the *enclosed operand*, and then a `)`. An expression enclosed in parentheses evaluates to the value of the enclosed operand. From 31dc83fe187a87af2b162801d50f4bed171fecdb Mon Sep 17 00:00:00 2001 From: "Havvy (Ryan Scheel)" Date: Wed, 3 Mar 2021 21:39:17 -0800 Subject: [PATCH 3/4] More editorial changes to expressions * Go back to calling the syntactic elements "expressions", but name them operands afterwards. * Use "called XYZ" to name something in syntax. * Trade out a few uses of expressions using letters for operand names for the actual operand names. * Pick arbitrary names for the operands. I don't like some of them, but they're not set in stone. * Say they're not set in stone in the main expressions chapter. --- src/expressions.md | 3 +++ src/expressions/array-expr.md | 22 +++++++++++++--------- src/expressions/await-expr.md | 10 +++++----- src/expressions/block-expr.md | 7 +++---- src/expressions/call-expr.md | 8 +++++--- src/expressions/closure-expr.md | 2 +- src/expressions/field-expr.md | 10 ++++------ src/expressions/grouped-expr.md | 7 +++++-- 8 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 5646e4691..aaa25af03 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -55,6 +55,9 @@ In this way, the structure of expressions dictates the structure of execution. Blocks are just another kind of expression, so blocks, statements, expressions, and blocks again can recursively nest inside each other to an arbitrary depth. +> **Note**: We give names to the operands of expressions so that we may discuss +> them, but these names are not stable and may be changed. + ## Expression precedence The precedence of Rust operators and expressions is ordered as follows, going diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index 7369d5378..5f50f09cb 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -14,21 +14,24 @@ Array expressions come in two forms. The first form lists out every value in the array. -The syntax for this form is a comma-separated list of operands of uniform type enclosed in square brackets. +The syntax for this form is a comma-separated list of expressions of uniform type enclosed in square brackets. This produces an array containing each of these values in the order they are written. -The syntax for the second form is two operands separated by a semicolon (`;`) enclosed in square brackets. -The operand after the `;` must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. -`[a; b]` creates an array containing `b` copies of the value of `a`. -If the operand after the semicolon has a value greater than 1 then this requires that the type of `a` is [`Copy`], or `a` must be a path to a constant item. +The syntax for the second form is two expressions separated by a semicolon (`;`) enclosed in square brackets. +The expression before the `;` is called the *repeat operand*. +The expression after the `;` is called the *length operand*. +It must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. +An array expression of this form creates an array with the length of the value of the legnth operand with each element a copy of the repeat operand. +That is, `[a; b]` creates an array containing `b` copies of the value of `a`. +If the length operand has a value greater than 1 then this requires that the type of the repeat operand is [`Copy`] or that it must be a [path] to a constant item. -When the repeat expression, `a`, is a constant item, it is evaluated `b` times. -If `b` is 0, the constant item is not evaluated at all. -For expressions that are not a constant item, it is evaluated exactly once, and then the result is copied `b` times. +When the repeat operand is a constant item, it is evaluated the length operand's value times. +If that value is `0`, then the constant item is not evaluated at all. +For expressions that are not a constant item, it is evaluated exactly once, and then the result is copied the length operand's value times.
-Warning: In the case where `b` is 0, and `a` is a non-constant item, there is currently a bug in `rustc` where the value `a` is evaluated but not dropped, thus causing a leak. +Warning: In the case where the length operand is 0, and the repeat operand is a non-constant item, there is currently a bug in `rustc` where the value `a` is evaluated but not dropped, thus causing a leak. See [issue #74836](https://github.com/rust-lang/rust/issues/74836).
@@ -95,4 +98,5 @@ The array index expression can be implemented for types other than arrays and sl [constant item]: ../items/constant-items.md [literal]: ../tokens.md#literals [memory location]: ../expressions.md#place-expressions-and-value-expressions +[path]: path-expr.md [slice]: ../types/slice.md diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md index dacfa83e2..8ea89f739 100644 --- a/src/expressions/await-expr.md +++ b/src/expressions/await-expr.md @@ -5,12 +5,12 @@ >    [_Expression_] `.` `await` *Await expressions* suspend the current computation until the given future is ready to produce a value. -The syntax for an await expression is an operand with a type that implements the Future trait, `.`, and then the `await` keyword. +The syntax for an await expression is an expression with a type that implements the Future trait, called the *future operand*, then the token `.`, and then the `await` keyword. Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block]. -More specifically, an `.await` expression has the following effect. +More specifically, an await expression has the following effect. -1. Evaluate `` to a [future] `tmp`; +1. Evaluate the future operand to a [future] `tmp`; 2. Pin `tmp` using [`Pin::new_unchecked`]; 3. This pinned future is then polled by calling the [`Future::poll`] method and passing it the current [task context](#task-context); 3. If the call to `poll` returns [`Poll::Pending`], then the future returns `Poll::Pending`, suspending its state so that, when the surrounding async context is re-polled,execution returns to step 2; @@ -25,11 +25,11 @@ Because `await` expressions are only legal in an async context, there must be so ## Approximate desugaring -Effectively, an `.await` expression is roughly equivalent to the following non-normative desugaring: +Effectively, an await expression is roughly equivalent to the following non-normative desugaring: ```rust,ignore -match /* */ { +match future_operand { mut pinned => loop { let mut pin = unsafe { Pin::new_unchecked(&mut pinned) }; match Pin::future::poll(Pin::borrow(&mut pin), &mut current_context) { diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index 1cfad4874..fdaf36b41 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -16,7 +16,7 @@ A *block expression*, or *block*, is a control flow expression and anonymous nam As a control flow expression, a block sequentially executes its component non-item declaration statements and then its final optional expression. As an anonymous namespace scope, item declarations are only in scope inside the block itself and variables declared by `let` statements are in scope from the next statement until the end of the block. -The syntax for a block is `{`, then any [inner attributes], then [statements], then an optional operand, and finally a `}`. +The syntax for a block is `{`, then any [inner attributes], then any number of [statements], then an optional expression, called the final operand, and finally a `}`. Statements are usually required to be followed by a semicolon, with two exceptions: @@ -25,9 +25,9 @@ Statements are usually required to be followed by a semicolon, with two exceptio Furthermore, extra semicolons between statements are allowed, but these semicolons do not affect semantics. When evaluating a block expression, each statement, except for item declaration statements, is executed sequentially. -Then the final expression is executed, if given. +Then the final operand is executed, if given. -The type of a block is the type of the final expression, or `()` if the final expression is omitted. +The type of a block is the type of the final operand, or `()` if the final operand is omitted. ```rust # fn fn_call() {} @@ -47,7 +47,6 @@ assert_eq!(5, five); Blocks are always [value expressions] and evaluate the last operand in value expression context. - > **Note**: This can be used to force moving a value if really needed. > For example, the following example fails on the call to `consume_self` because the struct was moved out of `s` in the block expression. > diff --git a/src/expressions/call-expr.md b/src/expressions/call-expr.md index c9b590114..577f3f432 100644 --- a/src/expressions/call-expr.md +++ b/src/expressions/call-expr.md @@ -8,11 +8,11 @@ >    [_Expression_] ( `,` [_Expression_] )\* `,`? A *call expression* calls a function. -The syntax of a call expression is an operand, the *function operand*, followed by a parenthesized comma-separated list of operands, the *argument operands*. +The syntax of a call expression is an expression, called the *function operand*, followed by a parenthesized comma-separated list of expression, called the *argument operands*. If the function eventually returns, then the expression completes. -For [non-function types](../types/function-item.md), the expression f(...) uses the method on one of the [`std::ops::Fn`], [`std::ops::FnMut`] or [`std::ops::FnOnce`] traits, which differ in whether they take the type by reference, mutable reference, or take ownership respectively. +For [non-function types], the expression `f(...)` uses the method on one of the [`std::ops::Fn`], [`std::ops::FnMut`] or [`std::ops::FnOnce`] traits, which differ in whether they take the type by reference, mutable reference, or take ownership respectively. An automatic borrow will be taken if needed. -`f` will also be automatically dereferences as required. +The function operand will also be [automatically dereferenced] as required. Some examples of call expressions: @@ -92,4 +92,6 @@ Refer to [RFC 132] for further details and motivations. [`std::ops::FnMut`]: ../../std/ops/trait.FnMut.html [`std::ops::FnOnce`]: ../../std/ops/trait.FnOnce.html [`std::ops::Fn`]: ../../std/ops/trait.Fn.html +[automatically dereferenced]: field-expr.md#automatic-dereferencing [fully-qualified syntax]: ../paths.md#qualified-paths +[non-function types]: ../types/function-item.md diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 1b9d9df21..5a415f3b8 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -13,7 +13,7 @@ >    [_OuterAttribute_]\* [_Pattern_] ( `:` [_Type_] )? A *closure expression*, also know as a lambda expression or a lambda, defines a [closure type] and evaluates to a value of that type. -The syntax for a closure expression is an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, the *return type*, and then an operand, the *closure body*. +The syntax for a closure expression is an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], called the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, called the *return type*, and then an expression, called the *closure body operand*. The optional type after each pattern is a type annotation for the pattern. If there is a return type, the closure body must be a [block]. diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index bddcf0c64..736fc5551 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -7,8 +7,8 @@ A *field expression* is a [place expression] that evaluates to the location of a field of a [struct] or [union]. When the operand is [mutable], the field expression is also mutable. -The syntax for a field expression is an operand, then a `.`, and finally an [identifier]. -Field expressions cannot be followed by a parenthetical comma-separated list of expressions, as that is parsed as a [method call expression]. +The syntax for a field expression is an expression, called the container operand*, then a `.`, and finally an [identifier]. +Field expressions cannot be followed by a parenthetical comma-separated list of expressions, as that is instead parsed as a [method call expression]. That is, they cannot be the function operand of a [call expression]. > **Note**: Wrap the field expression in a [parenthesized expression] to use it in a call expression. @@ -24,9 +24,7 @@ That is, they cannot be the function operand of a [call expression]. > (holds_callable.callable)(); > ``` -A field expression denotes a field of a [struct] or [union]. - -To call a function stored in a struct, parentheses are needed around the field expression. +Examples: ```rust,ignore @@ -38,7 +36,7 @@ foo().x; ## Automatic dereferencing -If the type of the operand implements [`Deref`] or [`DerefMut`][`Deref`] depending on whether the operand is [mutable], it is *automatically dereferenced* as many times as necessary to make the field access possible. +If the type of the container operand implements [`Deref`] or [`DerefMut`][`Deref`] depending on whether the operand is [mutable], it is *automatically dereferenced* as many times as necessary to make the field access possible. This processes is also called *autoderef* for short. ## Borrowing diff --git a/src/expressions/grouped-expr.md b/src/expressions/grouped-expr.md index 0b4197d18..d76e9a1e0 100644 --- a/src/expressions/grouped-expr.md +++ b/src/expressions/grouped-expr.md @@ -5,9 +5,11 @@ >    `(` [_InnerAttribute_]\* [_Expression_] `)` A *parenthesized expression* wraps a single expression, evaluating to that expression. -The syntax for a parenthesized expression is a `(`, then an operand, the *enclosed operand*, and then a `)`. +The syntax for a parenthesized expression is a `(`, then an expression, called the *enclosed operand*, and then a `)`. -An expression enclosed in parentheses evaluates to the value of the enclosed operand. +Parenthesized expressions evaluate to the value of the enclosed operand. +Unlike other expressions, parenthesized expressions are both [place expressions and value expressions][place]. +When the enclosed operand is a place expression, it is a place expression and when the enclosed operand is a value expression, it is a value expression. Parentheses can be used to explicitly modify the precedence order of subexpressions within an expression. @@ -45,3 +47,4 @@ assert_eq!((a.f)(), "The field f"); [_Expression_]: ../expressions.md [_InnerAttribute_]: ../attributes.md [attributes on block expressions]: block-expr.md#attributes-on-block-expressions +[place]: ../expressions.md#place-expressions-and-value-expressions From 23672971a16c69ea894bef24992b74912cfe5d25 Mon Sep 17 00:00:00 2001 From: Ryan Scheel Date: Sun, 7 Mar 2021 22:30:31 -0800 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Eric Huss --- src/expressions/await-expr.md | 2 +- src/expressions/block-expr.md | 1 + src/expressions/field-expr.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md index 8ea89f739..e7fc45d27 100644 --- a/src/expressions/await-expr.md +++ b/src/expressions/await-expr.md @@ -5,7 +5,7 @@ >    [_Expression_] `.` `await` *Await expressions* suspend the current computation until the given future is ready to produce a value. -The syntax for an await expression is an expression with a type that implements the Future trait, called the *future operand*, then the token `.`, and then the `await` keyword. +The syntax for an await expression is an expression with a type that implements the [Future] trait, called the *future operand*, then the token `.`, and then the `await` keyword. Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block]. More specifically, an await expression has the following effect. diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index fdaf36b41..deece94ca 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -22,6 +22,7 @@ Statements are usually required to be followed by a semicolon, with two exceptio 1. Item declaration statements do not need to be followed by a semicolon. 2. Expression statements usually require a following semicolon except if its outer expression is a flow control expression. + Furthermore, extra semicolons between statements are allowed, but these semicolons do not affect semantics. When evaluating a block expression, each statement, except for item declaration statements, is executed sequentially. diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index 736fc5551..93777b3a7 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -7,7 +7,7 @@ A *field expression* is a [place expression] that evaluates to the location of a field of a [struct] or [union]. When the operand is [mutable], the field expression is also mutable. -The syntax for a field expression is an expression, called the container operand*, then a `.`, and finally an [identifier]. +The syntax for a field expression is an expression, called the *container operand*, then a `.`, and finally an [identifier]. Field expressions cannot be followed by a parenthetical comma-separated list of expressions, as that is instead parsed as a [method call expression]. That is, they cannot be the function operand of a [call expression].