From 1ef1d865a4ae4e09e0dc5451c4190e92d291ddf8 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 10:42:21 -0400 Subject: [PATCH 01/14] Add paragraph identifiers to expressions.md --- src/expressions.md | 58 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/expressions.md b/src/expressions.md index 1ee015f07..36c08a45e 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -1,5 +1,8 @@ # Expressions +r[expr] + +r[expr.syntax] > **Syntax**\ > _Expression_ :\ >       _ExpressionWithoutBlock_\ @@ -43,15 +46,23 @@ >       | [_MatchExpression_]\ >    ) +r[expr.intro] An expression may have two roles: it always produces a *value*, and it may have *effects* (otherwise known as "side effects"). + +r[expr.evaluation] An expression *evaluates to* a value, and has effects during *evaluation*. + +r[expr.operands] Many expressions contain sub-expressions, called the *operands* of the expression. + +r[expr.behaviour] The meaning of each kind of expression dictates several things: * Whether or not to evaluate the operands when evaluating the expression * The order in which to evaluate the operands * How to combine the operands' values to obtain the value of the expression +r[expr.structure] 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. @@ -59,6 +70,8 @@ Blocks are just another kind of expression, so blocks, statements, expressions, ## Expression precedence +r[expr.precedence] + The precedence of Rust operators and expressions is ordered as follows, going from strong to weak. Binary Operators at the same precedence level are grouped in the order given by their associativity. @@ -86,6 +99,9 @@ Binary Operators at the same precedence level are grouped in the order given by ## Evaluation order of operands +r[expr.operand-order] + +r[expr.operand-order.default] The following list of expressions all evaluate their operands the same way, as described after the list. Other expressions either don't take operands or evaluate them conditionally as described on their respective pages. @@ -109,6 +125,7 @@ Other expressions either don't take operands or evaluate them conditionally as d * Range expression * Return expression +r[expr.operand-order.operands-before-primary] The operands of these expressions are evaluated prior to applying the effects of the expression. Expressions taking multiple operands are evaluated left to right as written in the source code. @@ -132,17 +149,27 @@ assert_eq!( ## Place Expressions and Value Expressions +r[expr.place-value] + +r[expr.place-value.intro] 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. +r[expr.place-value.place-memory-location] A *place expression* is an expression that represents a memory location. + +r[expr.place-value.place-expr-kinds] These expressions are [paths] which refer to local variables, [static variables], [dereferences][deref] (`*expr`), [array indexing] expressions (`expr[expr]`), [field] references (`expr.f`) and parenthesized place expressions. + +r[expr.place-value.value-expr-kinds] All other expressions are value expressions. +r[expr.place-value.value-result] A *value expression* is an expression that represents an actual value. +r[expr.place-value.place-context] The following contexts are *place expression* contexts: * The left operand of a [compound assignment] expression. @@ -157,6 +184,7 @@ The following contexts are *place expression* contexts: > Note: Historically, place expressions were called *lvalues* and value expressions were called *rvalues*. +r[expr.place-value.assignee] An *assignee expression* is an expression that appears in the left operand of an [assignment][assign] expression. Explicitly, the assignee expressions are: @@ -169,13 +197,23 @@ Explicitly, the assignee expressions are: fields). - [Unit structs][_StructExpression_]. +r[expr.place-value.parentehesis] Arbitrary parenthesisation is permitted inside assignee expressions. ### Moved and copied types +r[expr.move] + +r[expr.move.intro] When a place expression is evaluated in a value expression context, or is bound by value in a pattern, it denotes the value held _in_ that memory location. + +r[expr.move.copy] If the type of that value implements [`Copy`], then the value will be copied. + +r[expr.move.requires-sized] In the remaining situations, if that type is [`Sized`], then it may be possible to move the value. + +r[expr.move.movable-place] Only the following place expressions may be moved out of: * [Variables] which are not currently borrowed. @@ -183,15 +221,22 @@ Only the following place expressions may be moved out of: * [Fields][field] of a place expression which can be moved out of and don't implement [`Drop`]. * The result of [dereferencing][deref] an expression with type [`Box`] and that can also be moved out of. +r[expr.move.deinitialization] After moving out of a place expression that evaluates to a local variable, the location is deinitialized and cannot be read from again until it is reinitialized. + +r[expr.move.place-invalid] In all other cases, trying to use a place expression in a value expression context is an error. ### Mutability +r[expr.mut] + +r[expr.mut.intro] For a place expression to be [assigned][assign] to, mutably [borrowed][borrow], [implicitly mutably borrowed], or bound to a pattern containing `ref mut`, it must be _mutable_. We call these *mutable place expressions*. In contrast, other place expressions are called *immutable place expressions*. +r[expr.mut.valid-places] The following expressions can be mutable place expression contexts: * Mutable [variables] which are not currently borrowed. @@ -208,12 +253,17 @@ The following expressions can be mutable place expression contexts: ### Temporaries +r[expr.temporary] + When using a value expression in most place expression contexts, a temporary unnamed memory location is created and initialized to that value. The expression evaluates to that location instead, except if [promoted] to a `static`. The [drop scope] of the temporary is usually the end of the enclosing statement. ### Implicit Borrows +r[expr.implicit-borrow] + +r[expr.implicit-borrow-intro] Certain expressions will treat an expression as a place expression by implicitly borrowing it. For example, it is possible to compare two unsized [slices][slice] for equality directly, because the `==` operator implicitly borrows its operands: @@ -230,6 +280,7 @@ let b: &[i32]; ::std::cmp::PartialEq::eq(&*a, &*b); ``` +r[expr.implicit-borrow.application] Implicit borrows may be taken in the following expressions: * Left operand in [method-call] expressions. @@ -242,11 +293,16 @@ Implicit borrows may be taken in the following expressions: ## Overloading Traits +r[expr.overload] + Many of the following operators and expressions can also be overloaded for other types using traits in `std::ops` or `std::cmp`. These traits also exist in `core::ops` and `core::cmp` with the same names. ## Expression Attributes +r[expr.attr] + +r[expr.attr.restriction] [Outer attributes][_OuterAttribute_] before an expression are allowed only in a few specific cases: * Before an expression used as a [statement]. @@ -254,11 +310,11 @@ These traits also exist in `core::ops` and `core::cmp` with the same names. * The tail expression of [block expressions]. +r[expr.attr.never-before] They are never allowed before: * [Range][_RangeExpression_] expressions. * Binary operator expressions ([_ArithmeticOrLogicalExpression_], [_ComparisonExpression_], [_LazyBooleanExpression_], [_TypeCastExpression_], [_AssignmentExpression_], [_CompoundAssignmentExpression_]). - [block expressions]: expressions/block-expr.md [call expressions]: expressions/call-expr.md [field]: expressions/field-expr.md From f6c0b44b9c59e76cdbed9f8a37e44cc48b5d1461 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 11:10:38 -0400 Subject: [PATCH 02/14] Add identifier syntax to array-expr through block-expr --- src/expressions/array-expr.md | 33 +++++++++++++++++++++ src/expressions/await-expr.md | 13 +++++++++ src/expressions/block-expr.md | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index 52d31940d..d20942ada 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -1,7 +1,10 @@ # Array and array index expressions +r[expr.array] + ## Array expressions +r[expr.array.syntax] > **Syntax**\ > _ArrayExpression_ :\ >    `[` _ArrayElements_? `]` @@ -10,23 +13,45 @@ >       [_Expression_] ( `,` [_Expression_] )\* `,`?\ >    | [_Expression_] `;` [_Expression_] +r[expr.array.constructor] *Array expressions* construct [arrays][array]. Array expressions come in two forms. +r[expr.array.array] The first form lists out every value in the array. + +r[expr.array.array-syntax] The syntax for this form is a comma-separated list of expressions of uniform type enclosed in square brackets. + +r[expr.array.array-behaviour] This produces an array containing each of these values in the order they are written. +r[expr.array.repeat] The syntax for the second form is two expressions separated by a semicolon (`;`) enclosed in square brackets. + +r[expr.array.repeat-operand] The expression before the `;` is called the *repeat operand*. + +r[expr.array.length-operand] The expression after the `;` is called the *length operand*. + +r[expr.array.length-restriction] It must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. + +r[expr.array.repeat-behaviour] An array expression of this form creates an array with the length of the value of the length operand with each element being a copy of the repeat operand. That is, `[a; b]` creates an array containing `b` copies of the value of `a`. + +r[expr.array.repeat-copy] 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. +r[expr.array.repeat-const-item] When the repeat operand is a constant item, it is evaluated the length operand's value times. + +r[expr.array.repeat-evaluation-zero] If that value is `0`, then the constant item is not evaluated at all. + +r[expr.array.repeat-non-const] 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. ```rust @@ -41,17 +66,24 @@ const EMPTY: Vec = Vec::new(); ## Array and slice indexing expressions +r[expr.array.index] + > **Syntax**\ > _IndexExpression_ :\ >    [_Expression_] `[` [_Expression_] `]` +r[expr.array.index.array] [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. +r[expr.array.index.trait] 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. Just as with methods, Rust will also insert dereference operations on `a` repeatedly to find an implementation. +r[expr.array.index.zero-index] Indices are zero-based for arrays and slices. + +r[expr.array.index.const] Array access is a [constant expression], so bounds can be checked at compile-time with a constant index value. Otherwise a check will be performed at run-time that will put the thread in a _panicked state_ if it fails. @@ -73,6 +105,7 @@ let arr = ["a", "b"]; arr[10]; // warning: index out of bounds ``` +r[expr.array.index.trait-impl] The array index expression can be implemented for types other than arrays and slices by implementing the [Index] and [IndexMut] traits. [`Copy`]: ../special-types-and-traits.md#copy diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md index ae129890f..319b9188e 100644 --- a/src/expressions/await-expr.md +++ b/src/expressions/await-expr.md @@ -1,15 +1,24 @@ # Await expressions +r[expr.await] + +r[expr.await.syntax] > **Syntax**\ > _AwaitExpression_ :\ >    [_Expression_] `.` `await` +r[expr.await.intro] An `await` expression is a syntactic construct for suspending a computation provided by an implementation of `std::future::IntoFuture` until the given future is ready to produce a value. + +r[expr.await.construct] The syntax for an await expression is an expression with a type that implements the [`IntoFuture`] trait, called the *future operand*, then the token `.`, and then the `await` keyword. + +r[expr.await.constraints] Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block]. +r[expr.await.effects] More specifically, an await expression has the following effect. 1. Create a future by calling [`IntoFuture::into_future`] on the future operand. @@ -23,11 +32,15 @@ More specifically, an await expression has the following effect. ## Task context +r[expr.await.task] + 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. ## Approximate desugaring +r[expr.await.desugar] + Effectively, an await expression is roughly equivalent to the following non-normative desugaring: diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index 55936e38b..560f74971 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -1,5 +1,8 @@ # Block expressions +r[expr.block] + +r[expr.block.syntax] > **Syntax**\ > _BlockExpression_ :\ >    `{`\ @@ -12,23 +15,35 @@ >    | [_Statement_]\+ [_ExpressionWithoutBlock_]\ >    | [_ExpressionWithoutBlock_] +r[expr.block.intro] A *block expression*, or *block*, is a control flow expression and anonymous namespace scope for items and variable declarations. + +r[expr.block.sequential-evaluation] As a control flow expression, a block sequentially executes its component non-item declaration statements and then its final optional expression. + +r[expr.block.namepsace] 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. See the [scopes] chapter for more details. +r[expr.block.inner-attributes] 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 `}`. +r[expr.block.statements] 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. +r[expr.block.null-statement] Furthermore, extra semicolons between statements are allowed, but these semicolons do not affect semantics. +r[expr.block.evaluation] When evaluating a block expression, each statement, except for item declaration statements, is executed sequentially. + +r[expr.block.result] Then the final operand is executed, if given. +r[expr.block.type] The type of a block is the type of the final operand, or `()` if the final operand is omitted. ```rust @@ -47,6 +62,7 @@ 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. +r[expr.block.value] 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. @@ -73,16 +89,27 @@ Blocks are always [value expressions] and evaluate the last operand in value exp ## `async` blocks +r[expr.block.async] + +r[expr.block.async.syntax] > **Syntax**\ > _AsyncBlockExpression_ :\ >    `async` `move`? _BlockExpression_ +r[expr.block.async.intro] An *async block* is a variant of a block expression which evaluates to a future. + +r[expr.block.async.future-result] The final expression of the block, if present, determines the result value of the future. +r[expr.block.async.anonymous-type] Executing an async block is similar to executing a closure expression: its immediate effect is to produce and return an anonymous type. + +r[expr.block.async.future] Whereas closures return a type that implements one or more of the [`std::ops::Fn`] traits, however, the type returned for an async block implements the [`std::future::Future`] trait. + +r[expr.block.async.layout-unspecified] The actual data format for this type is unspecified. > **Note:** The future type that rustc generates is roughly equivalent to an enum with one variant per `await` point, where each variant stores the data needed to resume from its corresponding point. @@ -91,22 +118,32 @@ The actual data format for this type is unspecified. ### Capture modes +r[expr.block.async.capture] + 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. ### Async context +r[expr.block.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. ### Control-flow operators +r[expr.block.async.function] + +r[expr.block.async.function.intro] Async blocks act like a function boundary, much like closures. + +r[expr.block.async.function.return-try] Therefore, the `?` operator and `return` expressions both affect the output of the future, not the enclosing function or other context. That is, `return ` from within an async block will return the result of `` as the output of the future. Similarly, if `?` propagates an error, that error is propagated as the result of the future. +r[expr.block.async.function.control-flow] Finally, the `break` and `continue` keywords cannot be used to branch out from an async block. Therefore the following is illegal: @@ -120,15 +157,21 @@ loop { ## `const` blocks +r[expr.block.const] + +r[expr.block.const.syntax] > **Syntax**\ > _ConstBlockExpression_ :\ >    `const` _BlockExpression_ +r[expr.block.const.intro] A *const block* is a variant of a block expression whose body evaluates at compile-time instead of at runtime. +r[expr.block.const.context] Const blocks allows you to define a constant value without having to define new [constant items], and thus they are also sometimes referred as *inline consts*. It also supports type inference so there is no need to specify the type, unlike [constant items]. +r[expr.block.const.generic-params] Const blocks have the ability to reference generic parameters in scope, unlike [free][free item] constant items. They are desugared to constant items with generic parameters in scope (similar to associated constants, but without a trait or type they are associated with). For example, this code: @@ -153,6 +196,8 @@ fn foo() -> usize { } ``` +r[expr.block.const.evaluation] + If the const block expression is executed at runtime, then the constant is guaranteed to be evaluated, even if its return value is ignored: ```rust @@ -166,6 +211,8 @@ fn foo() -> usize { } ``` +r[expr.block.const.not-executed] + If the const block expression is not executed at runtime, it may or may not be evaluated: ```rust,compile_fail if false { @@ -176,6 +223,8 @@ if false { ## `unsafe` blocks +r[expr.block.unsafe] + > **Syntax**\ > _UnsafeBlockExpression_ :\ >    `unsafe` _BlockExpression_ @@ -199,10 +248,15 @@ let a = unsafe { an_unsafe_fn() }; ## Labelled block expressions +r[expr.block.label] + Labelled block expressions are documented in the [Loops and other breakable expressions] section. ## Attributes on block expressions +r[expr.block.attributes] + +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. @@ -213,6 +267,7 @@ Labelled block expressions are documented in the [Loops and other breakable expr * A block expression as the tail expression of another block expression. +r[expr.block.attributes.valid] The attributes that have meaning on a block expression are [`cfg`] and [the lint check attributes]. For example, this function returns `true` on unix platforms and `false` on other platforms. From 2714dea3866a63ee516a26c5edb40dcd0f196a0c Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 11:14:46 -0400 Subject: [PATCH 03/14] Add identifier syntax to call-expr --- src/expressions/call-expr.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/expressions/call-expr.md b/src/expressions/call-expr.md index 64df58ce4..71abcf5ba 100644 --- a/src/expressions/call-expr.md +++ b/src/expressions/call-expr.md @@ -1,5 +1,8 @@ # Call expressions +r[expr.call] + +r[expr.call.syntax] > **Syntax**\ > _CallExpression_ :\ >    [_Expression_] `(` _CallParams_? `)` @@ -7,10 +10,17 @@ > _CallParams_ :\ >    [_Expression_] ( `,` [_Expression_] )\* `,`? +r[expr.call.intro] A *call expression* calls a function. 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*. + +r[expr.call.convergence] If the function eventually returns, then the expression completes. + +r[expr.call.trait] 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. + +r[expr.call.autoref-deref] An automatic borrow will be taken if needed. The function operand will also be [automatically dereferenced] as required. @@ -24,13 +34,19 @@ let name: &'static str = (|| "Rust")(); ## Disambiguating Function Calls +r[expr.call.desugar] + +r[expr.call.desugar.fully-qualified] All function calls are sugar for a more explicit [fully-qualified syntax]. + +r[expr.call.desugar.ambiguity] 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. > We mention them here for searchability's sake. +r[expr.call.desugar.limits] Several situations often occur which result in ambiguities about the receiver or referent of method or associated function calls. These situations may include: @@ -38,6 +54,7 @@ These situations may include: * Auto-`deref` is undesirable; for example, distinguishing between methods on a smart pointer itself and the pointer's referent * Methods which take no arguments, like [`default()`], and return properties of a type, like [`size_of()`] +r[expr.call.desugar.explicit-path] To resolve the ambiguity, the programmer may refer to their desired method or function using more specific paths, types, or traits. For example, From 20588628ed5b30f875465e15dbfbcb2b817c657f Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 12:25:46 -0400 Subject: [PATCH 04/14] Add identifier syntax to closure-expr, field-expr, and grouped-expr --- src/expressions/closure-expr.md | 22 ++++++++++++++++++++++ src/expressions/field-expr.md | 13 +++++++++++++ src/expressions/grouped-expr.md | 8 ++++++++ 3 files changed, 43 insertions(+) diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 103f74795..67db7b312 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -1,5 +1,8 @@ # Closure expressions +r[expr.closure] + +r[expr.closure.syntax] > **Syntax**\ > _ClosureExpression_ :\ >    `move`?\ @@ -12,23 +15,40 @@ > _ClosureParam_ :\ >    [_OuterAttribute_]\* [_PatternNoTopAlt_] ( `:` [_Type_] )? +r[expr.closure.intro] A *closure expression*, also known 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], 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*. + +r[expr.closure.param-type] The optional type after each pattern is a type annotation for the pattern. + +r[expr.closure.explicit-type-body] If there is a return type, the closure body must be a [block]. +r[expr.closure.parameter-restriction] 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 closure parameters are irrefutable [patterns], whose type annotation is optional and will be inferred from context if not given. + +r[expr.closure.unique-type] Each closure expression has a unique, anonymous type. +r[expr.closure.captures] Significantly, closure expressions _capture their environment_, which regular [function definitions] do not. + +r[expr.closure.capture-inference] 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. + +r[expr.closure.capture-mut-ref] 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. + +r[expr.closure.capture-move] 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 lifetime is `'static`. ## Closure trait implementations +r[expr.closure.trait-impl] + 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. @@ -54,6 +74,8 @@ ten_times(move |j| println!("{}, {}", word, j)); ## Attributes on closure parameters +r[expr.closure.param-attributes] + Attributes on closure parameters follow the same rules and restrictions as [regular function parameters]. [_Expression_]: ../expressions.md diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index 11c2d3d3d..6f325ebfd 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -1,13 +1,22 @@ # Field access expressions +r[expr.field] + +r[expr.field.syntax] > **Syntax**\ > _FieldExpression_ :\ >    [_Expression_] `.` [IDENTIFIER] +r[expr.field.intro] A *field expression* is a [place expression] that evaluates to the location of a field of a [struct] or [union]. + +r[expr.field.mut] When the operand is [mutable], the field expression is also mutable. +r[expr.field.form] The syntax for a field expression is an expression, called the *container operand*, then a `.`, and finally an [identifier]. + +r[expr.field.constraint] 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]. @@ -36,11 +45,15 @@ foo().x; ## Automatic dereferencing +r[expr.field.autoref-deref] + 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 process is also called *autoderef* for short. ## Borrowing +r[expr.field.borrow] + 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`]. diff --git a/src/expressions/grouped-expr.md b/src/expressions/grouped-expr.md index 1ae2209c5..77d499657 100644 --- a/src/expressions/grouped-expr.md +++ b/src/expressions/grouped-expr.md @@ -1,16 +1,24 @@ # Grouped expressions +r[expr.paren] + +r[expr.paren.syntax] > **Syntax**\ > _GroupedExpression_ :\ >    `(` [_Expression_] `)` +r[expr.paren.intro] A *parenthesized expression* wraps a single expression, evaluating to that expression. The syntax for a parenthesized expression is a `(`, then an expression, called the *enclosed operand*, and then a `)`. +r[expr.paren.evaluation] Parenthesized expressions evaluate to the value of the enclosed operand. + +r[expr.paren.place-or-value] 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. +r[expr.paren.overridew-precedence] Parentheses can be used to explicitly modify the precedence order of subexpressions within an expression. An example of a parenthesized expression: From 23cfde56826cb86cf5b472317c3adb0950a0a891 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 13:14:17 -0400 Subject: [PATCH 05/14] Add identifier syntax to if-expr and literal-expr --- src/expressions/if-expr.md | 30 ++++++++++ src/expressions/literal-expr.md | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index fe387c828..583ca44bc 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -1,7 +1,10 @@ # `if` and `if let` expressions +r[expr.if] + ## `if` expressions +r[expr.if.syntax] > **Syntax**\ > _IfExpression_ :\ >    `if` [_Expression_]_except struct expression_ [_BlockExpression_]\ @@ -10,13 +13,26 @@ > | _IfExpression_ > | _IfLetExpression_ ) )\? +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. + +r[expr.if.constraint] The condition operands must have the [boolean type]. + +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. + +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. + +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. + +r[expr.if.type] An `if` expression must have the same type in all situations. ```rust @@ -39,6 +55,9 @@ assert_eq!(y, "Bigger"); ## `if let` expressions +r[expr.if.let] + +r[expr.if.let.syntax] > **Syntax**\ > _IfLetExpression_ :\ >    `if` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ @@ -48,9 +67,16 @@ assert_eq!(y, "Bigger"); > | _IfExpression_ > | _IfLetExpression_ ) )\? +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. + +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. ```rust @@ -74,6 +100,7 @@ if let _ = 5 { } ``` +r[expr.if.let.else-if] `if` and `if let` expressions can be intermixed: ```rust @@ -90,6 +117,7 @@ let a = if let Some(1) = x { assert_eq!(a, 3); ``` +r[expr.if.let.desugaring] An `if let` expression is equivalent to a [`match` expression] as follows: @@ -111,6 +139,7 @@ match EXPR { } ``` +r[expr.if.let.or-pattern] Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions: ```rust @@ -125,6 +154,7 @@ if let E::X(n) | E::Y(n) = v { } ``` +r[expr.if.let.restriction] 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: diff --git a/src/expressions/literal-expr.md b/src/expressions/literal-expr.md index 221690787..02d470725 100644 --- a/src/expressions/literal-expr.md +++ b/src/expressions/literal-expr.md @@ -1,5 +1,8 @@ # Literal expressions +r[expr.literal] + +r[expr.literal.syntax] > **Syntax**\ > _LiteralExpression_ :\ >       [CHAR_LITERAL]\ @@ -14,10 +17,13 @@ >    | [FLOAT_LITERAL]\ >    | `true` | `false` +r[expr.literal.intro] A _literal expression_ is an expression consisting of a single token, rather than a sequence of tokens, that immediately and directly denotes the value it evaluates to, rather than referring to it by name or some other evaluation rule. +r[expr.literal.const-expr] A literal is a form of [constant expression], so is evaluated (primarily) at compile time. +r[expr.literal.literal-token] Each of the lexical [literal][literal tokens] forms described earlier can make up a literal expression, as can the keywords `true` and `false`. ```rust @@ -26,14 +32,19 @@ Each of the lexical [literal][literal tokens] forms described earlier can make u 5; // integer type ``` +r[expr.literal.string-representation] In the descriptions below, the _string representation_ of a token is the sequence of characters from the input which matched the token's production in a *Lexer* grammar snippet. > **Note**: this string representation never includes a character `U+000D` (CR) immediately followed by `U+000A` (LF): this pair would have been previously transformed into a single `U+000A` (LF). ## Escapes +r[expr.literal.escape] + +r[expr.literal.escape.intro] The descriptions of textual literal expressions below make use of several forms of _escape_. +r[expr.literal.escape.sequence] Each form of escape is characterised by: * an _escape sequence_: a sequence of characters, which always begins with `U+005C` (`\`) * an _escaped value_: either a single character or an empty sequence of characters @@ -44,6 +55,8 @@ In the definitions of escapes below: ### Simple escapes +r[expr.literal.escape.simple] + Each sequence of characters occurring in the first column of the following table is an escape sequence. In each case, the escaped value is the character given in the corresponding entry in the second column. @@ -60,6 +73,8 @@ In each case, the escaped value is the character given in the corresponding entr ### 8-bit escapes +r[expr.literal.escape.hex-octet] + The escape sequence consists of `\x` followed by two hexadecimal digits. The escaped value is the character whose [Unicode scalar value] is the result of interpreting the final two characters in the escape sequence as a hexadecimal integer, as if by [`u8::from_str_radix`] with radix 16. @@ -68,12 +83,16 @@ The escaped value is the character whose [Unicode scalar value] is the result of ### 7-bit escapes +r[expr.literal.escape.hex-ascii] + The escape sequence consists of `\x` followed by an octal digit then a hexadecimal digit. The escaped value is the character whose [Unicode scalar value] is the result of interpreting the final two characters in the escape sequence as a hexadecimal integer, as if by [`u8::from_str_radix`] with radix 16. ### Unicode escapes +r[expr.literal.escape.unicode] + The escape sequence consists of `\u{`, followed by a sequence of characters each of which is a hexadecimal digit or `_`, followed by `}`. The escaped value is the character whose [Unicode scalar value] is the result of interpreting the hexadecimal digits contained in the escape sequence as a hexadecimal integer, as if by [`u32::from_str_radix`] with radix 16. @@ -82,6 +101,8 @@ The escaped value is the character whose [Unicode scalar value] is the result of ### String continuation escapes +r[expr.literal.continuation] + The escape sequence consists of `\` followed immediately by `U+000A` (LF), and all following whitespace characters before the next non-whitespace character. For this purpose, the whitespace characters are `U+0009` (HT), `U+000A` (LF), `U+000D` (CR), and `U+0020` (SPACE). @@ -108,23 +129,33 @@ The escaped value is an empty sequence of characters. ## Character literal expressions +r[expr.literal.char] + +r[expr.literal.char.intro] A character literal expression consists of a single [CHAR_LITERAL] token. +r[expr.literal.char.type] The expression's type is the primitive [`char`][textual types] type. +r[expr.literal.char.restriction] The token must not have a suffix. +r[expr.literal.char.literal-content] The token's _literal content_ is the sequence of characters following the first `U+0027` (`'`) and preceding the last `U+0027` (`'`) in the string representation of the token. +r[expr.literal.char.represented] The literal expression's _represented character_ is derived from the literal content as follows: +r[expr.literal.char.escape] * If the literal content is one of the following forms of escape sequence, the represented character is the escape sequence's escaped value: * [Simple escapes] * [7-bit escapes] * [Unicode escapes] +r[expr.literal.char.single] * Otherwise the represented character is the single character that makes up the literal content. +r[expr.literal.char.result] The expression's value is the [`char`][textual types] corresponding to the represented character's [Unicode scalar value]. > **Note**: the permitted forms of a [CHAR_LITERAL] token ensure that these rules always produce a single character. @@ -140,17 +171,25 @@ Examples of character literal expressions: ## String literal expressions +r[expr.literal.string] + +r[expr.literal.string.intro] A string literal expression consists of a single [STRING_LITERAL] or [RAW_STRING_LITERAL] token. +r[expr.literal.string.type] The expression's type is a shared reference (with `static` lifetime) to the primitive [`str`][textual types] type. That is, the type is `&'static str`. +r[expr.literal.string.restriction] The token must not have a suffix. +r[expr.literal.string.literal-content] The token's _literal content_ is the sequence of characters following the first `U+0022` (`"`) and preceding the last `U+0022` (`"`) in the string representation of the token. +r[expr.literal.string.represented] The literal expression's _represented string_ is a sequence of characters derived from the literal content as follows: +r[expr.literal.string.escape] * If the token is a [STRING_LITERAL], each escape sequence of any of the following forms occurring in the literal content is replaced by the escape sequence's escaped value. * [Simple escapes] * [7-bit escapes] @@ -160,8 +199,10 @@ The literal expression's _represented string_ is a sequence of characters derive These replacements take place in left-to-right order. For example, the token `"\\x41"` is converted to the characters `\` `x` `4` `1`. +r[expr.literal.string.raw] * If the token is a [RAW_STRING_LITERAL], the represented string is identical to the literal content. +r[expr.literal.string.result] The expression's value is a reference to a statically allocated [`str`][textual types] containing the UTF-8 encoding of the represented string. Examples of string literal expressions: @@ -179,22 +220,32 @@ r##"foo #"# bar"##; // foo #"# bar ## Byte literal expressions +r[expr.literal.byte-char] + +r[expr.literal.byte-char.intro] A byte literal expression consists of a single [BYTE_LITERAL] token. +r[expr.literal.byte-char.literal] The expression's type is the primitive [`u8`][numeric types] type. +r[expr.literal.byte-char.restriction] The token must not have a suffix. +r[expr.literal.byte-char.literal-content] The token's _literal content_ is the sequence of characters following the first `U+0027` (`'`) and preceding the last `U+0027` (`'`) in the string representation of the token. +r[expr.literal.byte-char.represented] The literal expression's _represented character_ is derived from the literal content as follows: +r[expr.literal.byte-char.escape] * If the literal content is one of the following forms of escape sequence, the represented character is the escape sequence's escaped value: * [Simple escapes] * [8-bit escapes] +r[expr.literal.byte-char.single] * Otherwise the represented character is the single character that makes up the literal content. +r[expr.literal.byte-char.result] The expression's value is the represented character's [Unicode scalar value]. > **Note**: the permitted forms of a [BYTE_LITERAL] token ensure that these rules always produce a single character, whose Unicode scalar value is in the range of [`u8`][numeric types]. @@ -210,17 +261,25 @@ b'\xA0'; // 160 ## Byte string literal expressions +r[expr.literal.byte-string] + +r[expr.literal.byte-string.intro] A byte string literal expression consists of a single [BYTE_STRING_LITERAL] or [RAW_BYTE_STRING_LITERAL] token. +r[expr.literal.byte-string.type] The expression's type is a shared reference (with `static` lifetime) to an array whose element type is [`u8`][numeric types]. That is, the type is `&'static [u8; N]`, where `N` is the number of bytes in the represented string described below. +r[expr.literal.byte-string.restriction] The token must not have a suffix. +r[expr.literal.byte-string.literal-content] The token's _literal content_ is the sequence of characters following the first `U+0022` (`"`) and preceding the last `U+0022` (`"`) in the string representation of the token. +r[expr.literal.byte-string.represented] The literal expression's _represented string_ is a sequence of characters derived from the literal content as follows: +r[expr.literal.byte-string.escape] * If the token is a [BYTE_STRING_LITERAL], each escape sequence of any of the following forms occurring in the literal content is replaced by the escape sequence's escaped value. * [Simple escapes] * [8-bit escapes] @@ -229,8 +288,10 @@ The literal expression's _represented string_ is a sequence of characters derive These replacements take place in left-to-right order. For example, the token `b"\\x41"` is converted to the characters `\` `x` `4` `1`. +r[expr.literal.byte-string.raw] * If the token is a [RAW_BYTE_STRING_LITERAL], the represented string is identical to the literal content. +r[expr.literal.byte-string.result] The expression's value is a reference to a statically allocated array containing the [Unicode scalar values] of the characters in the represented string, in the same order. > **Note**: the permitted forms of [BYTE_STRING_LITERAL] and [RAW_BYTE_STRING_LITERAL] tokens ensure that these rules always produce array element values in the range of [`u8`][numeric types]. @@ -250,17 +311,25 @@ b"\\x52"; br"\x52"; // \x52 ## C string literal expressions +r[expr.literal.c-string] + +r[expr.literal.c-string.intro] A C string literal expression consists of a single [C_STRING_LITERAL] or [RAW_C_STRING_LITERAL] token. +r[expr.literal.c-string.type] The expression's type is a shared reference (with `static` lifetime) to the standard library [CStr] type. That is, the type is `&'static core::ffi::CStr`. +r[expr.literal.c-string.restriction] The token must not have a suffix. +r[expr.literal.c-string.literal-content] The token's _literal content_ is the sequence of characters following the first `"` and preceding the last `"` in the string representation of the token. +r[expr.literal.c-string.represented] The literal expression's _represented bytes_ are a sequence of bytes derived from the literal content as follows: +r[expr.literal.c-string.escape] * If the token is a [C_STRING_LITERAL], the literal content is treated as a sequence of items, each of which is either a single Unicode character other than `\` or an [escape]. The sequence of items is converted to a sequence of bytes as follows: * Each single Unicode character contributes its UTF-8 representation. @@ -269,10 +338,12 @@ The sequence of items is converted to a sequence of bytes as follows: * Each [unicode escape] contributes the UTF-8 representation of its escaped value. * Each [string continuation escape] contributes no bytes. +r[expr.literal.c-string.raw] * If the token is a [RAW_C_STRING_LITERAL], the represented bytes are the UTF-8 encoding of the literal content. > **Note**: the permitted forms of [C_STRING_LITERAL] and [RAW_C_STRING_LITERAL] tokens ensure that the represented bytes never include a null byte. +r[expr.literal.c-string.result] The expression's value is a reference to a statically allocated [CStr] whose array of bytes contains the represented bytes followed by a null byte. Examples of C string literal expressions: @@ -297,16 +368,24 @@ c"\u{00E6}".to_bytes(); // [195, 166] ## Integer literal expressions +r[expr.literal.int] + +r[expr.literal.int.intro] An integer literal expression consists of a single [INTEGER_LITERAL] token. +r[expr.literal.int.suffix] If the token has a [suffix], the suffix must be the name of one of the [primitive integer types][numeric types]: `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`, `usize`, or `isize`, and the expression has that type. +r[expr.literal.int.infer] If the token has no suffix, the expression's type is determined by type inference: +r[expr.literal.int.inference-unique-type] * If an integer type can be _uniquely_ determined from the surrounding program context, the expression has that type. +r[expr.literal.int.inference-default] * If the program context under-constrains the type, it defaults to the signed 32-bit integer `i32`. +r[expr.literal.int.inference-error] * If the program context over-constrains the type, it is considered a static type error. Examples of integer literal expressions: @@ -330,8 +409,10 @@ let a: u64 = 123; // type u64 0usize; // type usize ``` +r[expr.literal.int.representation] The value of the expression is determined from the string representation of the token as follows: +r[expr.literal.int.radix] * An integer radix is chosen by inspecting the first two characters of the string, as follows: * `0b` indicates radix 2 @@ -339,15 +420,20 @@ The value of the expression is determined from the string representation of the * `0x` indicates radix 16 * otherwise the radix is 10. +r[expr.literal.int.radix-prefix-stripped] * If the radix is not 10, the first two characters are removed from the string. +r[expr.literal.int.type-suffix-stripped] * Any suffix is removed from the string. +r[expr.literal.int.separators-stripped] * Any underscores are removed from the string. +r[expr.literal.int.u128-value] * The string is converted to a `u128` value as if by [`u128::from_str_radix`] with the chosen radix. If the value does not fit in `u128`, it is a compiler error. +r[expr.literal.int.cast] * The `u128` value is converted to the expression's type via a [numeric cast]. > **Note**: The final cast will truncate the value of the literal if it does not fit in the expression's type. @@ -358,18 +444,26 @@ If the value does not fit in `u128`, it is a compiler error. ## Floating-point literal expressions +r[expr.literal.float] + +r[expr.literal.float.intro] A floating-point literal expression has one of two forms: * a single [FLOAT_LITERAL] token * a single [INTEGER_LITERAL] token which has a suffix and no radix indicator +r[expr.literal.float.suffix] If the token has a [suffix], the suffix must be the name of one of the [primitive floating-point types][floating-point types]: `f32` or `f64`, and the expression has that type. +r[expr.literal.float.infer] If the token has no suffix, the expression's type is determined by type inference: +r[expr.literal.float.inference-unique-type] * If a floating-point type can be _uniquely_ determined from the surrounding program context, the expression has that type. +r[expr.literal.float.inference-default] * If the program context under-constrains the type, it defaults to `f64`. +r[expr.literal.float.inference-error] * If the program context over-constrains the type, it is considered a static type error. Examples of floating-point literal expressions: @@ -383,12 +477,16 @@ Examples of floating-point literal expressions: let x: f64 = 2.; // type f64 ``` +r[expr.literal.float.result] The value of the expression is determined from the string representation of the token as follows: +r[expr.literal.float.type-suffix-stripped] * Any suffix is removed from the string. +r[expr.literal.float.separators-stripped] * Any underscores are removed from the string. +r[expr.literal.float.value] * The string is converted to the expression's type as if by [`f32::from_str`] or [`f64::from_str`]. > **Note**: `-1.0`, for example, is an application of the [negation operator] to the literal expression `1.0`, not a single floating-point literal expression. @@ -399,8 +497,12 @@ The value of the expression is determined from the string representation of the ## Boolean literal expressions +r[expr.literal.bool] + +r[expr.literal.bool.intro] A boolean literal expression consists of one of the keywords `true` or `false`. +r[expr.literal.bool.result] The expression's type is the primitive [boolean type], and its value is: * true if the keyword is `true` * false if the keyword is `false` From ab876137a407030bea25118daa66727725bc3ef0 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 14:50:35 -0400 Subject: [PATCH 06/14] Add identifier syntax to loop-expr and match-expr --- src/expressions/loop-expr.md | 75 +++++++++++++++++++++++++++++++++++ src/expressions/match-expr.md | 38 ++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 9366c7f8f..432d1a8b9 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -1,5 +1,8 @@ # Loops and other breakable expressions +r[expr.block] + +r[expr.loop.syntax] > **Syntax**\ > _LoopExpression_ :\ >    [_LoopLabel_]? (\ @@ -17,6 +20,7 @@ [_IteratorLoopExpression_]: #iterator-loops [_LabelBlockExpression_]: #labelled-block-expressions +r[expr.loop.intro] Rust supports five loop expressions: * A [`loop` expression](#infinite-loops) denotes an infinite loop. @@ -25,29 +29,47 @@ Rust supports five loop expressions: * 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). + +r[expr.loop.continue] All except labelled block expressions support [`continue` expressions](#continue-expressions). + +r[expr.loop.explicit-result] Only `loop` and labelled block expressions support [evaluation to non-trivial values](#break-and-loop-values). ## Infinite loops +r[expr.loop.infinite] + +r[expr.loop.infinite.syntax] > **Syntax**\ > _InfiniteLoopExpression_ :\ >    `loop` [_BlockExpression_] +r[expr.loop.infinite.intro] A `loop` expression repeats execution of its body continuously: `loop { println!("I live."); }`. +r[expr.loop.infinite.diverging] A `loop` expression without an associated `break` expression is diverging and has type [`!`](../types/never.md). + +r[expr.loop.infinite.break] A `loop` expression containing associated [`break` expression(s)](#break-expressions) may terminate, and must have type compatible with the value of the `break` expression(s). ## Predicate loops +r[expr.loop.while] + +r[expr.loop.while.syntax] > **Syntax**\ > _PredicateLoopExpression_ :\ >    `while` [_Expression_]_except struct expression_ [_BlockExpression_] +r[expr.loop.while.intro] A `while` loop begins by evaluating the [boolean] loop conditional operand. + +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. @@ -64,13 +86,19 @@ while i < 10 { ## Predicate pattern loops +r[expr.loop.while.let] + +r[expr.loop.while.let.syntax] > **Syntax**\ > [_PredicatePatternLoopExpression_] :\ >    `while` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ > [_BlockExpression_] +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. @@ -87,6 +115,7 @@ while let _ = 5 { } ``` +r[expr.loop.while.let.desugar] A `while let` loop is equivalent to a `loop` expression containing a [`match` expression] as follows. @@ -108,6 +137,7 @@ is equivalent to } ``` +r[expr.loop.while.let.or-pattern] Multiple patterns may be specified with the `|` operator. This has the same semantics as with `|` in `match` expressions: @@ -119,16 +149,23 @@ while let Some(v @ 1) | Some(v @ 2) = vals.pop() { } ``` +r[expr.loop.while.let.restriction] As is the case in [`if let` expressions], the scrutinee cannot be a [lazy boolean operator expression][_LazyBooleanOperatorExpression_]. ## Iterator loops +r[expr.loop.for] + +r[expr.loop.for.syntax] > **Syntax**\ > _IteratorLoopExpression_ :\ >    `for` [_Pattern_] `in` [_Expression_]_except struct expression_ > [_BlockExpression_] +r[expr.loop.for.intro] A `for` expression is a syntactic construct for looping over elements provided by an implementation of `std::iter::IntoIterator`. + +r[expr.loop.for.condition] If the iterator yields a value, that value is matched against the irrefutable pattern, the body of the loop is executed, and then control returns to the head of the `for` loop. If the iterator is empty, the `for` expression completes. @@ -152,6 +189,7 @@ for n in 1..11 { assert_eq!(sum, 55); ``` +r[expr.loop.for.desugar] A `for` loop is equivalent to a `loop` expression containing a [`match` expression] as follows: @@ -181,7 +219,9 @@ is equivalent to } ``` +r[expr.loop.for.lang-items] `IntoIterator`, `Iterator`, and `Option` are always the standard library items here, not whatever those names resolve to in the current scope. + The variable names `next`, `iter`, and `val` are for exposition only, they do not actually have names the user can type. > **Note**: that the outer `match` is used to ensure that any [temporary values] in `iter_expr` don't get dropped before the loop is finished. @@ -189,14 +229,21 @@ The variable names `next`, `iter`, and `val` are for exposition only, they do no ## Loop labels +r[expr.loop.label] + +r[expr.loop.label.syntax] > **Syntax**\ > _LoopLabel_ :\ >    [LIFETIME_OR_LABEL] `:` +r[expr.loop.label.intro] A loop expression may optionally have a _label_. The label is written as a lifetime preceding the loop expression, as in `'foo: loop { break 'foo; }`, `'bar: while false {}`, `'humbug: for _ in 0..0 {}`. + +r[expr.loop.label.control-flow] If a label is present, then labeled `break` and `continue` expressions nested within this loop may exit out of this loop or return control to its head. See [break expressions](#break-expressions) and [continue expressions](#continue-expressions). +r[expr.loop.label.ref] Labels follow the hygiene and shadowing rules of local variables. For example, this code will print "outer loop": ```rust @@ -213,10 +260,14 @@ Labels follow the hygiene and shadowing rules of local variables. For example, t ## `break` expressions +r[expr.loop.break] + +r[expr.loop.break.syntax] > **Syntax**\ > _BreakExpression_ :\ >    `break` [LIFETIME_OR_LABEL]? [_Expression_]? +r[expr.loop.break.intro] When `break` is encountered, execution of the associated loop body is immediately terminated, for example: ```rust @@ -230,6 +281,7 @@ for x in 1..100 { assert_eq!(last, 12); ``` +r[expr.loop.break.label] A `break` expression is normally associated with the innermost `loop`, `for` or `while` loop enclosing the `break` expression, but a [label](#loop-labels) can be used to specify which enclosing loop is affected. Example: @@ -242,16 +294,24 @@ Example: } ``` +r[expr.loop.break.value] A `break` expression is only permitted in the body of a loop, and has one of the forms `break`, `break 'label` or ([see below](#break-and-loop-values)) `break EXPR` or `break 'label EXPR`. ## Labelled block expressions +r[expr.loop.block-labels] + > **Syntax**\ > _LabelBlockExpression_ :\ >    [_BlockExpression_] +r[expr.loop.block-labels.intro] Labelled block expressions are exactly like block expressions, except that they allow using `break` expressions within the block. + +r[expr.loop.block-labels.break] Unlike loops, `break` expressions within a labelled block expression *must* have a label (i.e. the label is not optional). + +r[expr.loop.block-labels.restriction] Similarly, labelled block expressions *must* begin with a label. ```rust @@ -275,19 +335,33 @@ let result = 'block: { ## `continue` expressions +r[expr.loop.continue] + +r[expr.loop.continue.syntax] > **Syntax**\ > _ContinueExpression_ :\ >    `continue` [LIFETIME_OR_LABEL]? +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. + +r[expr.loop.continue.for] In the case of a `for` loop, the head is the call-expression controlling the loop. +r[expr.loop.continue.label] Like `break`, `continue` is normally associated with the innermost enclosing loop, but `continue 'label` may be used to specify the loop affected. + +r[expr.loop.continue.constraint] A `continue` expression is only permitted in the body of a loop. ## `break` and loop values +r[expr.loop.break-value] + +r[expr.loop.break-value.intro] When associated with a `loop`, a break expression may be used to return a value from that loop, via one of the forms `break EXPR` or `break 'label EXPR`, where `EXPR` is an expression whose result is returned from the `loop`. For example: @@ -305,6 +379,7 @@ let result = loop { assert_eq!(result, 13); ``` +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 `()`. diff --git a/src/expressions/match-expr.md b/src/expressions/match-expr.md index edbc63658..e31b4158b 100644 --- a/src/expressions/match-expr.md +++ b/src/expressions/match-expr.md @@ -1,5 +1,8 @@ # `match` expressions +r[expr.match] + +r[expr.match.syntax] > **Syntax**\ > _MatchExpression_ :\ >    `match` _Scrutinee_ `{`\ @@ -23,15 +26,24 @@ > _MatchArmGuard_ :\ >    `if` [_Expression_] +r[expr.match.intro] A *`match` expression* branches on a pattern. The exact form of matching that occurs depends on the [pattern]. + +r[expr.match.scrutinee] A `match` expression has a *[scrutinee] expression*, which is the value to compare to the patterns. + +r[expr.match.scrutinee-constraint] The scrutinee expression and the patterns must have the same type. +r[expr.match.scrutinee-behaviour] A `match` behaves differently depending on whether or not the scrutinee expression is a [place expression or value expression][place expression]. + +r[expr.match.scrutinee-value] If the scrutinee expression is a [value expression], it is first evaluated into a temporary location, and the resulting value is sequentially compared to the patterns in the arms until a match is found. The first arm with a matching pattern is chosen as the branch target of the `match`, any variables bound by the pattern are assigned to local variables in the arm's block, and control enters the block. +r[expr.match.scrutinee-place] When the scrutinee expression is a [place expression], the match does not allocate a temporary location; however, a by-value binding may copy or move from the memory location. When possible, it is preferable to match on place expressions, as the lifetime of these matches inherits the lifetime of the place expression rather than being restricted to the inside of the match. @@ -51,9 +63,13 @@ match x { } ``` +r[expr.match.pattern-vars] Variables bound within the pattern are scoped to the match guard and the arm's expression. + +r[expr.match.pattern-var-binding] The [binding mode] (move, copy, or reference) depends on the pattern. +r[expr.match.or-pattern] Multiple match patterns may be joined with the `|` operator. Each pattern will be tested in left-to-right sequence until a successful match is found. @@ -79,16 +95,27 @@ match S(1, 2) { > Note: The `2..=9` is a [Range Pattern], not a [Range Expression]. > Thus, only those types of ranges supported by range patterns can be used in match arms. +r[expr.match.or-patterns-restriction] Every binding in each `|` separated pattern must appear in all of the patterns in the arm. + +r[expr.match.binding-restriction] Every binding of the same name must have the same type, and have the same binding mode. ## Match guards +r[expr.match.guard] + +r[expr.match.guard.intro] Match arms can accept _match guards_ to further refine the criteria for matching a case. + +r[expr.match.guard.type] Pattern guards appear after the pattern and consist of a `bool`-typed expression following the `if` keyword. +r[expr.match.guard.behaviour] When the pattern matches successfully, the pattern guard expression is executed. If the expression evaluates to true, the pattern is successfully matched against. + +r[expr.match.guard.next] Otherwise, the next pattern, including other matches with the `|` operator in the same arm, is tested. ```rust @@ -115,18 +142,29 @@ let message = match maybe_digit { > assert_eq!(i.get(), 2); > ``` +r[expr.match.guard.bound-variables] A pattern guard may refer to the variables bound within the pattern they follow. + +r[expr.match.guard.shared-ref] Before evaluating the guard, a shared reference is taken to the part of the scrutinee the variable matches on. While evaluating the guard, this shared reference is then used when accessing the variable. + +r[expr.match.guard.value] Only when the guard evaluates to true is the value moved, or copied, from the scrutinee into the variable. This allows shared borrows to be used inside guards without moving out of the scrutinee in case guard fails to match. + +r[expr.match.guard.restriction] Moreover, by holding a shared reference while evaluating the guard, mutation inside guards is also prevented. ## Attributes on match arms +r[expr.match.attributes] + +r[expr.match.attributes.outer] Outer attributes are allowed on match arms. The only attributes that have meaning on match arms are [`cfg`] and the [lint check attributes]. +r[expr.match.attributes.inner] [Inner attributes] are allowed directly after the opening brace of the match expression in the same expression contexts as [attributes on block expressions]. [_Expression_]: ../expressions.md From cf5ad64ffac84284cad59a8e256368b6b860a576 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 15:20:49 -0400 Subject: [PATCH 07/14] Add identifier syntax to method-call-expr and operator-expr --- src/expressions/method-call-expr.md | 14 +++ src/expressions/operator-expr.md | 171 ++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 7f21c497b..8bdc06039 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -1,10 +1,16 @@ # Method-call expressions +r[expr.method] + +r[expr.method.syntax] > **Syntax**\ > _MethodCallExpression_ :\ >    [_Expression_] `.` [_PathExprSegment_] `(`[_CallParams_]? `)` +r[expr.method.intro] A _method call_ consists of an expression (the *receiver*) followed by a single dot, an expression path segment, and a parenthesized expression-list. + +r[expr.method.target] Method calls are resolved to associated [methods] on specific traits, either statically dispatching to a method if the exact `self`-type of the left-hand-side is known, or dynamically dispatching if the left-hand-side expression is an indirect [trait object](../types/trait-object.md). ```rust @@ -13,16 +19,21 @@ let log_pi = pi.unwrap_or(1.0).log(2.72); # assert!(1.14 < log_pi && log_pi < 1.15) ``` +r[expr.method.autoref-deref] When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method. This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call. The following procedure is used: +r[expr.method.candidate-recievers] The first step is to build a list of candidate receiver types. Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion] at the end, and adding the result type if that is successful. + +r[expr.method.candidate-recievers-refs] Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`. For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types will be `Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion), `&[i32]`, and finally `&mut [i32]`. +r[expr.method.candidate-search] Then, for each candidate type `T`, search for a [visible] method with a receiver of that type in the following places: 1. `T`'s inherent methods (methods implemented directly on `T`). @@ -58,11 +69,14 @@ Then, for each candidate type `T`, search for a [visible] method with a receiver > } > ``` +r[expr.method.ambiguious-target] If this results in multiple possible candidates, then it is an error, and the receiver must be [converted][disambiguate call] to an appropriate receiver type to make the method call. +r[expr.method.constraint] This process does not take into account the mutability or lifetime of the receiver, or whether a method is `unsafe`. Once a method is looked up, if it can't be called for one (or more) of those reasons, the result is a compiler error. +r[expr.method.ambiguious-search] If a step is reached where there is more than one possible method, such as where generic methods or traits are considered the same, then it is a compiler error. These cases require a [disambiguating function call syntax] for method and function invocation. diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index ea3984158..57ed999f0 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -1,5 +1,8 @@ # Operator expressions +r[expr.operator] + +r[expr.operator.syntax] > **Syntax**\ > _OperatorExpression_ :\ >       [_BorrowExpression_]\ @@ -13,19 +16,32 @@ >    | [_AssignmentExpression_]\ >    | [_CompoundAssignmentExpression_] +r[expr.operator.intro] Operators are defined for built in types by the Rust language. + +r[expr.operator.trait] Many of the following operators can also be overloaded using traits in `std::ops` or `std::cmp`. ## Overflow +r[expr.operator.int-overflow] + +r[expr.operator.int-overflow.intro] Integer operators will panic when they overflow when compiled in debug mode. The `-C debug-assertions` and `-C overflow-checks` compiler flags can be used to control this more directly. The following things are considered to be overflow: +r[expr.operator.int-overflow.binary-arith] * When `+`, `*` or binary `-` create a value greater than the maximum value, or less than the minimum value that can be stored. + +r[expr.operator.int-overflow.unary-neg] * Applying unary `-` to the most negative value of any signed integer type, unless the operand is a [literal expression] (or a literal expression standing alone inside one or more [grouped expressions][grouped expression]). + +r[expr.operator.int-overflow.div] * Using `/` or `%`, where the left-hand argument is the smallest integer of a signed integer type and the right-hand argument is `-1`. These checks occur even when `-C overflow-checks` is disabled, for legacy reasons. + +r[expr.operator.int-overflow.shift] * Using `<<` or `>>` where the right-hand argument is greater than or equal to the number of bits in the type of the left-hand argument, or is negative. > **Note**: The exception for literal expressions behind unary `-` means that forms such as `-128_i8` or `let j: i8 = -(128)` never cause a panic and have the expected value of -128. @@ -38,6 +54,8 @@ The following things are considered to be overflow: ## Borrow operators +r[expr.operator.borrow] + > **Syntax**\ > _BorrowExpression_ :\ >       (`&`|`&&`) [_Expression_]\ @@ -45,12 +63,21 @@ The following things are considered to be overflow: >    | (`&`|`&&`) `raw` `const` [_Expression_]\ >    | (`&`|`&&`) `raw` `mut` [_Expression_] +r[expr.operator.borrow.intro] The `&` (shared borrow) and `&mut` (mutable borrow) operators are unary prefix operators. + +r[expr.operator.borrow.result] When applied to a [place expression], this expressions produces a reference (pointer) to the location that the value refers to. + +r[expr.operator.borrow.lifetime] The memory location is also placed into a borrowed state for the duration of the reference. For a shared borrow (`&`), this implies that the place may not be mutated, but it may be read or shared again. For a mutable borrow (`&mut`), the place may not be accessed in any way until the borrow expires. + +r[expr.operator.borrow.mut] `&mut` evaluates its operand in a mutable place expression context. + +r[expr.operator.borrow.temporary] If the `&` or `&mut` operators are applied to a [value expression], then a [temporary value] is created. These operators cannot be overloaded. @@ -68,6 +95,7 @@ let mut array = [-2, 3, 9]; } ``` +r[expr.borrow.and-and-syntax] Even though `&&` is a single token ([the lazy 'and' operator](#lazy-boolean-operators)), when used in the context of borrow expressions it works as two borrows: ```rust @@ -83,10 +111,18 @@ let a = & & & & mut 10; ### Raw borrow operators +r[expr.borrow.raw] + +r[expr.borrow.raw.intro] `&raw const` and `&raw mut` are the *raw borrow operators*. + +r[expr.borrow.raw.place] The operand expression of these operators is evaluated in place expression context. + +r[expr.borrow.raw.result] `&raw const expr` then creates a const raw pointer of type `*const T` to the given place, and `&raw mut expr` creates a mutable raw pointer of type `*mut T`. +r[expr.borrow.raw.invalid-ref] The raw borrow operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions. In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed. @@ -125,15 +161,26 @@ let init = unsafe { uninit.assume_init() }; ## The dereference operator +r[expr.deref] + +r[expr.deref.syntax] > **Syntax**\ > _DereferenceExpression_ :\ >    `*` [_Expression_] +r[expr.deref.intro] The `*` (dereference) operator is also a unary prefix operator. + +r[expr.deref.result] When applied to a [pointer](../types/pointer.md) it denotes the pointed-to location. + +r[expr.deref.mut] If the expression is of type `&mut T` or `*mut T`, and is either a local variable, a (nested) field of a local variable or is a mutable [place expression], then the resulting memory location can be assigned to. + +r[expr.deref.safety] Dereferencing a raw pointer requires `unsafe`. +r[expr.deref.traits] On non-pointer types `*x` is equivalent to `*std::ops::Deref::deref(&x)` in an [immutable place expression context](../expressions.md#mutability) and `*std::ops::DerefMut::deref_mut(&mut x)` in a mutable place expression context. ```rust @@ -146,15 +193,27 @@ assert_eq!(*y, 11); ## The question mark operator +r[expr.try] + + +r[expr.try.syntax] > **Syntax**\ > _ErrorPropagationExpression_ :\ >    [_Expression_] `?` +r[expr.try.intro] The question mark operator (`?`) unwraps valid values or returns erroneous values, propagating them to the calling function. + +r[expr.try.constraint] It is a unary postfix operator that can only be applied to the types `Result` and `Option`. +r[expr.try.behaviour-std-result] When applied to values of the `Result` type, it propagates errors. + +r[expr.try.effects-err] If the value is `Err(e)`, then it will return `Err(From::from(e))` from the enclosing function or closure. + +r[expr.try.result-ok] If applied to `Ok(x)`, then it will unwrap the value to evaluate to `x`. ```rust @@ -170,8 +229,13 @@ println!("{:?}", res); # assert!(res.is_err()) ``` +r[expr.try.behaviour-std-option] When applied to values of the `Option` type, it propagates `None`s. + +r[expr.try.effects-none] If the value is `None`, then it will return `None`. + +r[expr.try.result-ok] If applied to `Some(x)`, then it will unwrap the value to evaluate to `x`. ```rust @@ -188,16 +252,23 @@ fn try_option_none() -> Option { assert_eq!(try_option_none(), None); ``` +r[expr.try.trait] `?` cannot be overloaded. ## Negation operators +r[expr.negate] + +r[expr.negate.syntax] > **Syntax**\ > _NegationExpression_ :\ >       `-` [_Expression_]\ >    | `!` [_Expression_] +r[expr.negate.intro] These are the last two unary operators. + +r[expr.negate.results] This table summarizes the behavior of them on primitive types and which traits are used to overload these operators for other types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in [value expression context][value expression] so are moved or copied. @@ -220,6 +291,9 @@ assert_eq!(true, !false); ## Arithmetic and Logical Binary Operators +r[expr.arith-logic] + +r[expr.arith-logic.syntax] > **Syntax**\ > _ArithmeticOrLogicalExpression_ :\ >       [_Expression_] `+` [_Expression_]\ @@ -233,7 +307,10 @@ assert_eq!(true, !false); >    | [_Expression_] `<<` [_Expression_]\ >    | [_Expression_] `>>` [_Expression_] +r[expr.arith-logic.syntax] Binary operators expressions are all written with infix notation. + +r[expr.arith-logic.behaviour] This table summarizes the behavior of arithmetic and logical binary operators on primitive types and which traits are used to overload these operators for other types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in [value expression context][value expression] so are moved or copied. @@ -277,6 +354,9 @@ assert_eq!(-10 >> 2, -3); ## Comparison Operators +r[expr.cmp] + +r[expr.cmp.syntax] > **Syntax**\ > _ComparisonExpression_ :\ >       [_Expression_] `==` [_Expression_]\ @@ -286,11 +366,17 @@ assert_eq!(-10 >> 2, -3); >    | [_Expression_] `>=` [_Expression_]\ >    | [_Expression_] `<=` [_Expression_] +r[expr.cmp.intro] Comparison operators are also defined both for primitive types and many types in the standard library. + +r[expr.cmp.restriction] Parentheses are required when chaining comparison operators. For example, the expression `a == b == c` is invalid and may be written as `(a == b) == c`. +r[expr.cmp.trait] Unlike arithmetic and logical operators, the traits for overloading these operators are used more generally to show how a type may be compared and will likely be assumed to define actual comparisons by functions that use these traits as bounds. Many functions and macros in the standard library can then use that assumption (although not to ensure safety). + +r[expr.cmp.place] Unlike the arithmetic and logical operators above, these operators implicitly take shared borrows of their operands, evaluating them in [place expression context][place expression]: ```rust @@ -301,8 +387,11 @@ a == b; ::std::cmp::PartialEq::eq(&a, &b); ``` + This means that the operands don't have to be moved out of. +r[expr.cmp.behaviour] + | Symbol | Meaning | Overloading method | |--------|--------------------------|----------------------------| | `==` | Equal | `std::cmp::PartialEq::eq` | @@ -325,13 +414,19 @@ assert!("World" >= "Hello"); ## Lazy boolean operators +r[expr.bool-logic] + +r[expr.bool-logic.syntax] > **Syntax**\ > _LazyBooleanExpression_ :\ >       [_Expression_] `||` [_Expression_]\ >    | [_Expression_] `&&` [_Expression_] +r[expr.bool-logic.intro] The operators `||` and `&&` may be applied to operands of boolean type. The `||` operator denotes logical 'or', and the `&&` operator denotes logical 'and'. + +r[expr.bool-logic.conditional-evaluation] They differ from `|` and `&` in that the right-hand operand is only evaluated when the left-hand operand does not already determine the result of the expression. That is, `||` only evaluates its right-hand operand when the left-hand operand evaluates to `false`, and `&&` only when it evaluates to `true`. @@ -342,12 +437,17 @@ let y = false && panic!(); // false, doesn't evaluate `panic!()` ## Type cast expressions +r[expr.as] + +r[expr.as.syntax] > **Syntax**\ > _TypeCastExpression_ :\ >    [_Expression_] `as` [_TypeNoBounds_] +r[expr.as.intro] A type cast expression is denoted with the binary operator `as`. +r[expr.as.result] Executing an `as` expression casts the value on the left-hand side to the type on the right-hand side. An example of an `as` expression: @@ -362,6 +462,7 @@ fn average(values: &[f64]) -> f64 { } ``` +r[expr.as.coercions] `as` can be used to explicitly perform [coercions](../type-coercions.md), as well as the following additional casts. Any cast that does not fit either a coercion rule or an entry in the table is a compiler error. Here `*T` means either `*const T` or `*mut T`. `m` stands for optional `mut` in @@ -396,23 +497,38 @@ reference types and `mut` or `const` in pointer types. #### Numeric cast +r[expr.as.numeric] + +r[expr.as.numeric.int-same-size] * Casting between two integers of the same size (e.g. i32 -> u32) is a no-op (Rust uses 2's complement for negative values of fixed integers) + +r[expr.as.numeric.int-truncation] * Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will truncate + +r[expr.as.numeric.int-extension] * Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will * zero-extend if the source is unsigned * sign-extend if the source is signed + +r[expr.as.numeric.float-as-int] * Casting from a float to an integer will round the float towards zero * `NaN` will return `0` * Values larger than the maximum integer value, including `INFINITY`, will saturate to the maximum value of the integer type. * Values smaller than the minimum integer value, including `NEG_INFINITY`, will saturate to the minimum value of the integer type. + +r[expr.as.numeric.int-as-float] * Casting from an integer to float will produce the closest possible float \* * if necessary, rounding is according to `roundTiesToEven` mode \*\*\* * on overflow, infinity (of the same sign as the input) is produced * note: with the current set of numeric types, overflow can only happen on `u128 as f32` for values greater or equal to `f32::MAX + (0.5 ULP)` + +r[expr.as.numeric.float-widening] * Casting from an f32 to an f64 is perfect and lossless + +r[expr.as.numeric.float-narrowing] * Casting from an f64 to an f32 will produce the closest possible f32 \*\* * if necessary, rounding is according to `roundTiesToEven` mode \*\*\* * on overflow, infinity (of the same sign as the input) is produced @@ -431,6 +547,8 @@ halfway between two floating point numbers. #### Enum cast +r[expr.as.enum] + Casts an enum to its discriminant, then uses a numeric cast if needed. Casting is limited to the following kinds of enumerations: @@ -439,20 +557,27 @@ Casting is limited to the following kinds of enumerations: #### Primitive to integer cast +r[expr.as.bool-char-as-int] + * `false` casts to `0`, `true` casts to `1` * `char` casts to the value of the code point, then uses a numeric cast if needed. #### `u8` to `char` cast +r[expr.as.u8-as-char] Casts to the `char` with the corresponding code point. #### Pointer to address cast +r[expr.as.pointer-as-int] + Casting from a raw pointer to an integer produces the machine address of the referenced memory. If the integer type is smaller than the pointer type, the address may be truncated; using `usize` avoids this. #### Address to pointer cast +r[expr.as.int-as-pointer] + Casting from an integer to a raw pointer interprets the integer as a memory address and produces a pointer referencing that memory.
@@ -480,9 +605,15 @@ assert_eq!(values[1], 3); #### Pointer-to-pointer cast +r[expr.as.pointer] + +r[expr.as.pointer.behaviour] `*const T` / `*mut T` can be cast to `*const U` / `*mut U` with the following behavior: +r[expr.as.pointer.sized] - If `T` and `U` are both sized, the pointer is returned unchanged. + +r[expr.as.pointer.unsized] - If `T` and `U` are both unsized, the pointer is also returned unchanged. In particular, the metadata is preserved exactly. @@ -491,31 +622,51 @@ assert_eq!(values[1], 3); (e.g., casting `*const [u16]` to `*const [u8]` will result in a raw pointer which refers to an object of half the size of the original). The same holds for `str` and any compound type whose unsized tail is a slice type, such as `struct Foo(i32, [u8])` or `(u64, Foo)`. + +r[expr.as.pointer.discard-metadata] - If `T` is unsized and `U` is sized, the cast discards all metadata that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer. ## Assignment expressions +r[expr.assign] + +r[expr.assign.syntax] > **Syntax**\ > _AssignmentExpression_ :\ >    [_Expression_] `=` [_Expression_] +r[expr.assign.intro] An *assignment expression* moves a value into a specified place. +r[expr.assign.assignee] 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*. + +r[expr.assign.behaviour-basic] In its most basic form, an assignee expression is a [place expression], and we discuss this case first. + +r[expr.assign.behaviour-destructring] 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. ### Basic assignments +r[expr.assign.basic] + +r[expr.assign.evaluation-order] Evaluating assignment expressions begins by evaluating its operands. The assigned value operand is evaluated first, followed by the assignee expression. + +r[expr.assign.destructring-order] 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. +r[expr.assign.drop-target] It then has the effect of first [dropping] the value at the assigned place, unless the place is an uninitialized local variable or an uninitialized field of a local variable. + +r[expr.assign.behaviour] Next it either [copies or moves] the assigned value to the assigned place. +r[expr.assign.result] An assignment expression always produces [the unit value][unit]. Example: @@ -528,6 +679,9 @@ x = y; ### Destructuring assignments +r[expr.assign.destructure] + +r[expr.assign.destructure.intro] 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: @@ -537,9 +691,12 @@ let (mut a, mut b) = (0, 1); (b, a) = (a, b); ``` +r[expr.assign.destructure.restriction] 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. + +r[expr.assign.destructure.constraint] 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. @@ -574,14 +731,20 @@ Struct { x: a, y: b } = Struct { x: 3, y: 4}; } ``` +r[expr.assign.destructure.repeat-ident] Identifiers are not forbidden from being used multiple times in a single assignee expression. +r[expr.assign.destructure.discard-value] [Underscore expressions][_UnderscoreExpression_] and empty [range expressions][_RangeExpression_] may be used to ignore certain values, without binding them. +r[expr.assign.destructure.default-binding] Note that default binding modes do not apply for the desugared expression. ## Compound assignment expressions +r[expr.compound-assign] + +r[expr.compound-assign.syntax] > **Syntax**\ > _CompoundAssignmentExpression_ :\ >       [_Expression_] `+=` [_Expression_]\ @@ -595,6 +758,7 @@ Note that default binding modes do not apply for the desugared expression. >    | [_Expression_] `<<=` [_Expression_]\ >    | [_Expression_] `>>=` [_Expression_] +r[expr.compound-assign.intro] *Compound assignment expressions* combine arithmetic and logical binary operators with assignment expressions. For example: @@ -607,16 +771,22 @@ assert!(x == 6); The syntax of compound assignment is a [mutable] [place expression], the *assigned operand*, then one of the operators followed by an `=` as a single token (no whitespace), and then a [value expression], the *modifying operand*. +r[expr.compound-assign.place] Unlike other place operands, the assigned place operand must be a place expression. + +r[expr.compound-assign.constraint] Attempting to use a value expression is a compiler error rather than promoting it to a temporary. +r[expr.compound-assign.operand-order] Evaluation of compound assignment expressions depends on the types of the operators. +r[expr.compound-assign.primitive-order] If both types are primitives, then the modifying operand will be evaluated first followed by the assigned operand. It will then set the value of the assigned operand's place to the value of performing the operation of the operator with the values of the assigned operand and modifying operand. > **Note**: This is different than other expressions in that the right operand is evaluated before the left one. +r[expr.compound-assign.trait] Otherwise, this expression is syntactic sugar for calling the function of the overloading compound assignment trait of the operator (see the table earlier in this chapter). A mutable borrow of the assigned operand is automatically taken. @@ -640,6 +810,7 @@ fn example() { } ``` +r[expr.compound-assign.result] Like assignment expressions, compound assignment expressions always produce [the unit value][unit].
From e4327dfc98e5dd146b7fd5d0a2dff8481f6630c4 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 15:59:11 -0400 Subject: [PATCH 08/14] Add identifier syntax to path-expr, range-expr, return-expr, struct-expr, tuple-expr, and underscore-expr --- src/expressions/path-expr.md | 9 +++++++++ src/expressions/range-expr.md | 6 ++++++ src/expressions/return-expr.md | 6 ++++++ src/expressions/struct-expr.md | 24 ++++++++++++++++++++++++ src/expressions/tuple-expr.md | 24 ++++++++++++++++++++++++ src/expressions/underscore-expr.md | 11 +++++++++-- 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/expressions/path-expr.md b/src/expressions/path-expr.md index 0707e9d41..581e1e514 100644 --- a/src/expressions/path-expr.md +++ b/src/expressions/path-expr.md @@ -1,12 +1,20 @@ # Path expressions +r[expr.path] + +r[expr.path.syntax] > **Syntax**\ > _PathExpression_ :\ >       [_PathInExpression_]\ >    | [_QualifiedPathInExpression_] +r[expr.path.intro] A [path] used as an expression context denotes either a local variable or an item. + +r[expr.path.place] Path expressions that resolve to local or static variables are [place expressions], other paths are [value expressions]. + +r[expr.path.safety] Using a [`static mut`] variable requires an [`unsafe` block]. ```rust @@ -23,6 +31,7 @@ let push_integer = Vec::::push; let slice_reverse = <[i32]>::reverse; ``` +r[expr.path.const] Evaluation of associated constants is handled the same way as [`const` blocks]. [_PathInExpression_]: ../paths.md#paths-in-expressions diff --git a/src/expressions/range-expr.md b/src/expressions/range-expr.md index 37409a4cf..0896d3a0e 100644 --- a/src/expressions/range-expr.md +++ b/src/expressions/range-expr.md @@ -1,5 +1,8 @@ # Range expressions +r[expr.range] + +r[expr.range.syntax] > **Syntax**\ > _RangeExpression_ :\ >       _RangeExpr_\ @@ -27,6 +30,7 @@ > _RangeToInclusiveExpr_ :\ >    `..=` [_Expression_] +r[expr.range.behaviour] The `..` and `..=` operators will construct an object of one of the `std::ops::Range` (or `core::ops::Range`) variants, according to the following table: | Production | Syntax | Type | Range | @@ -49,6 +53,7 @@ Examples: ..=7; // std::ops::RangeToInclusive ``` +r[expr.range.equivalence] The following expressions are equivalent. ```rust @@ -58,6 +63,7 @@ let y = 0..10; assert_eq!(x, y); ``` +r[expr.range.for] Ranges can be used in `for` loops: ```rust diff --git a/src/expressions/return-expr.md b/src/expressions/return-expr.md index b1b3787d8..61808967d 100644 --- a/src/expressions/return-expr.md +++ b/src/expressions/return-expr.md @@ -1,10 +1,16 @@ # `return` expressions +r[expr.return] + +r[expr.return.syntax] > **Syntax**\ > _ReturnExpression_ :\ >    `return` [_Expression_]? +r[expr.return.intro] Return expressions are denoted with the keyword `return`. + +r[expr.return.behaviour] Evaluating a `return` expression moves its argument into the designated output location for the current function call, destroys the current function activation frame, and transfers control to the caller frame. An example of a `return` expression: diff --git a/src/expressions/struct-expr.md b/src/expressions/struct-expr.md index 8d9154789..8b715999b 100644 --- a/src/expressions/struct-expr.md +++ b/src/expressions/struct-expr.md @@ -1,5 +1,8 @@ # Struct expressions +r[expr.struct] + +r[expr.struct.syntax] > **Syntax**\ > _StructExpression_ :\ >       _StructExprStruct_\ @@ -29,6 +32,7 @@ > > _StructExprUnit_ : [_PathInExpression_] +r[expr.struct.intro] A *struct expression* creates a struct, enum, or union value. It consists of a path to a [struct], [enum variant], or [union] item followed by the values for the fields of the item. There are three forms of struct expressions: struct, tuple, and unit. @@ -51,17 +55,29 @@ some_fn::(Cookie); ## Field struct expression +r[expr.struct.field] + +r[expr.struct.field.intro] A struct expression with fields enclosed in curly braces allows you to specify the value for each individual field in any order. The field name is separated from its value with a colon. +r[expr.struct.field.union-constraint] A value of a [union] type can only be created using this syntax, and it must specify exactly one field. ## Functional update syntax +r[expr.struct.update] + +r[expr.struct.update.intro] A struct expression that constructs a value of a struct type can terminate with the syntax `..` followed by an expression to denote a functional update. + +r[expr.struct.update.constraint] The expression following `..` (the base) must have the same struct type as the new struct type being formed. +r[expr.struct.update.fields] The entire expression uses the given values for the fields that were specified and moves or copies the remaining fields from the base expression. + +r[expr.struct.update.visibility-constraint] As with all struct expressions, all of the fields of the struct must be [visible], even those not explicitly named. ```rust @@ -72,9 +88,11 @@ Point3d {y: 0, z: 10, .. base}; // OK, only base.x is accessed drop(y_ref); ``` +r[expr.struct.restriction] Struct expressions with curly braces can't be used directly in a [loop] or [if] expression's head, or in the [scrutinee] of an [if let] or [match] expression. However, struct expressions can be used in these situations if they are within another expression, for example inside [parentheses]. +r[expr.struct.tuple-field] The field names can be decimal integer values to specify indices for constructing tuple structs. This can be used with base structs to fill out the remaining indices not specified: @@ -87,6 +105,8 @@ let c3 = Color{1: 0, ..c2}; // Fill out all other fields using a base struct. ### Struct field init shorthand +r[expr.struct.field.named] + When initializing a data structure (struct, enum, union) with named (but not numbered) fields, it is allowed to write `fieldname` as a shorthand for `fieldname: fieldname`. This allows a compact syntax with less duplication. For example: @@ -102,6 +122,8 @@ Point3d { x, y: y_value, z }; ## Tuple struct expression +r[expr.struct.tuple] + A struct expression with fields enclosed in parentheses constructs a tuple struct. Though it is listed here as a specific expression for completeness, it is equivalent to a [call expression] to the tuple struct's constructor. For example: @@ -114,6 +136,8 @@ let pos = c(8, 6, 7); // Creates a `Position` value. ## Unit struct expression +r[expr.struct.unit] + A unit struct expression is just the path to a unit struct item. This refers to the unit struct's implicit constant of its value. The unit struct value can also be constructed with a fieldless struct expression. For example: diff --git a/src/expressions/tuple-expr.md b/src/expressions/tuple-expr.md index a6ac1c962..2986a5601 100644 --- a/src/expressions/tuple-expr.md +++ b/src/expressions/tuple-expr.md @@ -1,7 +1,12 @@ # Tuple and tuple indexing expressions +r[expr.tuple] + ## Tuple expressions +r[expr.tuple] + +r[expr.tuple.syntax] > **Syntax**\ > _TupleExpression_ :\ >    `(` _TupleElements_? `)` @@ -9,14 +14,25 @@ > _TupleElements_ :\ >    ( [_Expression_] `,` )+ [_Expression_]? +r[expr.tuple.result] A *tuple expression* constructs [tuple values][tuple type]. +r[expr.tuple.intro] The syntax for tuple expressions is a parenthesized, comma separated list of expressions, called the *tuple initializer operands*. + +r[expr.tuple.unary-tuple-restriction] 1-ary tuple expressions require a comma after their tuple initializer operand to be disambiguated with a [parenthetical expression]. +r[expr.tuple.value] Tuple expressions are a [value expression] that evaluate into a newly constructed value of a tuple type. + +r[expr.tuple.type] The number of tuple initializer operands is the arity of the constructed tuple. + +r[expr.tuple.unit] Tuple expressions without any tuple initializer operands produce the unit tuple. + +r[expr.tuple.fields] For other tuple expressions, the first written tuple initializer operand initializes the field `0` and subsequent operands initializes the next highest field. For example, in the tuple expression `('a', 'b', 'c')`, `'a'` initializes the value of the field `0`, `'b'` field `1`, and `'c'` field `2`. @@ -31,19 +47,27 @@ Examples of tuple expressions and their types: ## Tuple indexing expressions +r[expr.tuple-index] + +r[expr.tuple-index.syntax] > **Syntax**\ > _TupleIndexingExpression_ :\ >    [_Expression_] `.` [TUPLE_INDEX] +r[expr.tuple-index.intro] A *tuple indexing expression* accesses fields of [tuples][tuple type] and [tuple structs][tuple struct]. The syntax for a tuple index expression is an expression, called the *tuple operand*, then a `.`, then finally a tuple index. + +r[expr.tuple-index.restriction] The syntax for the *tuple index* is a [decimal literal] with no leading zeros, underscores, or suffix. For example `0` and `2` are valid tuple indices but not `01`, `0_`, nor `0i32`. +r[expr.tuple-index.constraint] The type of the tuple operand must be a [tuple type] or a [tuple struct]. The tuple index must be a name of a field of the type of the tuple operand. +r[expr.tuple-index.result] Evaluation of tuple index expressions has no side effects beyond evaluation of its tuple operand. As a [place expression], it evaluates to the location of the field of the tuple operand with the same name as the tuple index. diff --git a/src/expressions/underscore-expr.md b/src/expressions/underscore-expr.md index d68c6a998..d5b8fd35c 100644 --- a/src/expressions/underscore-expr.md +++ b/src/expressions/underscore-expr.md @@ -1,13 +1,20 @@ # `_` expressions +r[expr.placeholder] + +r[expr.placeholder.syntax] > **Syntax**\ > _UnderscoreExpression_ :\ >    `_` +r[expr.placeholder.intro] 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. +placeholder in a destructuring assignment. + +r[expr.placeholder.constraint] +They may only appear in the left-hand side of an assignment. +r[expr.placeholder.pattern] Note that this is distinct from the [wildcard pattern](../patterns.md#wildcard-pattern). Examples of `_` expressions: From 864813f1550fb5d2679598a77b880c7360b53760 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 16:02:30 -0400 Subject: [PATCH 09/14] Remove double-spaces and trailing whitespace --- src/expressions/loop-expr.md | 1 - src/expressions/method-call-expr.md | 1 - src/expressions/operator-expr.md | 3 --- 3 files changed, 5 deletions(-) diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 432d1a8b9..684317b55 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -94,7 +94,6 @@ r[expr.loop.while.let.syntax] >    `while` `let` [_Pattern_] `=` [_Scrutinee_]_except lazy boolean operator expression_ > [_BlockExpression_] - 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. diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 8bdc06039..1a98bd53b 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -86,7 +86,6 @@ These cases require a [disambiguating function call syntax] for method and funct > > This special case may be removed in the future. -
***Warning:*** For [trait objects], if there is an inherent method of the same name as a trait method, it will give a compiler error when trying to call the method in a method call expression. diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 57ed999f0..a9173d5f0 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -158,7 +158,6 @@ unsafe { f1_ptr.write(true); } let init = unsafe { uninit.assume_init() }; ``` - ## The dereference operator r[expr.deref] @@ -195,7 +194,6 @@ assert_eq!(*y, 11); r[expr.try] - r[expr.try.syntax] > **Syntax**\ > _ErrorPropagationExpression_ :\ @@ -387,7 +385,6 @@ a == b; ::std::cmp::PartialEq::eq(&a, &b); ``` - This means that the operands don't have to be moved out of. r[expr.cmp.behaviour] From 0d1b9ee3703dbb889fc76b368b6c110c2a4b3dce Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 16:06:10 -0400 Subject: [PATCH 10/14] Refer to `Some(x)` in `expr.try.result-some` --- src/expressions/operator-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index a9173d5f0..7868a09a5 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -233,7 +233,7 @@ When applied to values of the `Option` type, it propagates `None`s. r[expr.try.effects-none] If the value is `None`, then it will return `None`. -r[expr.try.result-ok] +r[expr.try.result-some] If applied to `Some(x)`, then it will unwrap the value to evaluate to `x`. ```rust From 9db0bfaeb06e33eadfb95cd82a495e9c01490da1 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 27 Aug 2024 16:09:02 -0400 Subject: [PATCH 11/14] Remove extraneous whitespace from literal-expr --- src/expressions/literal-expr.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/expressions/literal-expr.md b/src/expressions/literal-expr.md index 02d470725..6db9e0574 100644 --- a/src/expressions/literal-expr.md +++ b/src/expressions/literal-expr.md @@ -507,7 +507,6 @@ The expression's type is the primitive [boolean type], and its value is: * true if the keyword is `true` * false if the keyword is `false` - [Escape]: #escapes [Simple escape]: #simple-escapes [Simple escapes]: #simple-escapes From 2d73b476094999bb13e102b0b3ad1c94e7043a06 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 28 Aug 2024 12:08:33 -0400 Subject: [PATCH 12/14] Fix spelling from behaviour to behavior --- src/expressions.md | 2 +- src/expressions/array-expr.md | 4 ++-- src/expressions/match-expr.md | 4 ++-- src/expressions/operator-expr.md | 16 ++++++++-------- src/expressions/range-expr.md | 2 +- src/expressions/return-expr.md | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 36c08a45e..a275cd80a 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -55,7 +55,7 @@ An expression *evaluates to* a value, and has effects during *evaluation*. r[expr.operands] Many expressions contain sub-expressions, called the *operands* of the expression. -r[expr.behaviour] +r[expr.behavior] The meaning of each kind of expression dictates several things: * Whether or not to evaluate the operands when evaluating the expression diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index d20942ada..3617a91b4 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -23,7 +23,7 @@ The first form lists out every value in the array. r[expr.array.array-syntax] The syntax for this form is a comma-separated list of expressions of uniform type enclosed in square brackets. -r[expr.array.array-behaviour] +r[expr.array.array-behavior] This produces an array containing each of these values in the order they are written. r[expr.array.repeat] @@ -38,7 +38,7 @@ The expression after the `;` is called the *length operand*. r[expr.array.length-restriction] It must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. -r[expr.array.repeat-behaviour] +r[expr.array.repeat-behavior] An array expression of this form creates an array with the length of the value of the length operand with each element being a copy of the repeat operand. That is, `[a; b]` creates an array containing `b` copies of the value of `a`. diff --git a/src/expressions/match-expr.md b/src/expressions/match-expr.md index e31b4158b..28dba22b4 100644 --- a/src/expressions/match-expr.md +++ b/src/expressions/match-expr.md @@ -36,7 +36,7 @@ A `match` expression has a *[scrutinee] expression*, which is the value to compa r[expr.match.scrutinee-constraint] The scrutinee expression and the patterns must have the same type. -r[expr.match.scrutinee-behaviour] +r[expr.match.scrutinee-behavior] A `match` behaves differently depending on whether or not the scrutinee expression is a [place expression or value expression][place expression]. r[expr.match.scrutinee-value] @@ -111,7 +111,7 @@ Match arms can accept _match guards_ to further refine the criteria for matching r[expr.match.guard.type] Pattern guards appear after the pattern and consist of a `bool`-typed expression following the `if` keyword. -r[expr.match.guard.behaviour] +r[expr.match.guard.behavior] When the pattern matches successfully, the pattern guard expression is executed. If the expression evaluates to true, the pattern is successfully matched against. diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 7868a09a5..5ae5a1f4d 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -205,7 +205,7 @@ The question mark operator (`?`) unwraps valid values or returns erroneous value r[expr.try.constraint] It is a unary postfix operator that can only be applied to the types `Result` and `Option`. -r[expr.try.behaviour-std-result] +r[expr.try.behavior-std-result] When applied to values of the `Result` type, it propagates errors. r[expr.try.effects-err] @@ -227,7 +227,7 @@ println!("{:?}", res); # assert!(res.is_err()) ``` -r[expr.try.behaviour-std-option] +r[expr.try.behavior-std-option] When applied to values of the `Option` type, it propagates `None`s. r[expr.try.effects-none] @@ -308,7 +308,7 @@ r[expr.arith-logic.syntax] r[expr.arith-logic.syntax] Binary operators expressions are all written with infix notation. -r[expr.arith-logic.behaviour] +r[expr.arith-logic.behavior] This table summarizes the behavior of arithmetic and logical binary operators on primitive types and which traits are used to overload these operators for other types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in [value expression context][value expression] so are moved or copied. @@ -387,7 +387,7 @@ a == b; This means that the operands don't have to be moved out of. -r[expr.cmp.behaviour] +r[expr.cmp.behavior] | Symbol | Meaning | Overloading method | |--------|--------------------------|----------------------------| @@ -604,7 +604,7 @@ assert_eq!(values[1], 3); r[expr.as.pointer] -r[expr.as.pointer.behaviour] +r[expr.as.pointer.behavior] `*const T` / `*mut T` can be cast to `*const U` / `*mut U` with the following behavior: r[expr.as.pointer.sized] @@ -638,10 +638,10 @@ An *assignment expression* moves a value into a specified place. r[expr.assign.assignee] 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*. -r[expr.assign.behaviour-basic] +r[expr.assign.behavior-basic] In its most basic form, an assignee expression is a [place expression], and we discuss this case first. -r[expr.assign.behaviour-destructring] +r[expr.assign.behavior-destructring] 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. ### Basic assignments @@ -660,7 +660,7 @@ For destructuring assignment, subexpressions of the assignee expression are eval r[expr.assign.drop-target] It then has the effect of first [dropping] the value at the assigned place, unless the place is an uninitialized local variable or an uninitialized field of a local variable. -r[expr.assign.behaviour] +r[expr.assign.behavior] Next it either [copies or moves] the assigned value to the assigned place. r[expr.assign.result] diff --git a/src/expressions/range-expr.md b/src/expressions/range-expr.md index 0896d3a0e..eba097dd3 100644 --- a/src/expressions/range-expr.md +++ b/src/expressions/range-expr.md @@ -30,7 +30,7 @@ r[expr.range.syntax] > _RangeToInclusiveExpr_ :\ >    `..=` [_Expression_] -r[expr.range.behaviour] +r[expr.range.behavior] The `..` and `..=` operators will construct an object of one of the `std::ops::Range` (or `core::ops::Range`) variants, according to the following table: | Production | Syntax | Type | Range | diff --git a/src/expressions/return-expr.md b/src/expressions/return-expr.md index 61808967d..c0226a8ab 100644 --- a/src/expressions/return-expr.md +++ b/src/expressions/return-expr.md @@ -10,7 +10,7 @@ r[expr.return.syntax] r[expr.return.intro] Return expressions are denoted with the keyword `return`. -r[expr.return.behaviour] +r[expr.return.behavior] Evaluating a `return` expression moves its argument into the designated output location for the current function call, destroys the current function activation frame, and transfers control to the caller frame. An example of a `return` expression: From 3055d0d5646d99ec7d9ddbf3ab3ec8c8e232f26e Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 28 Aug 2024 12:12:59 -0400 Subject: [PATCH 13/14] Fix mdbook-spec identifiers to be unique --- src/expressions/loop-expr.md | 4 ++-- src/expressions/operator-expr.md | 2 +- src/expressions/tuple-expr.md | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 684317b55..240cfad6c 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -1,6 +1,6 @@ # Loops and other breakable expressions -r[expr.block] +r[expr.loop] r[expr.loop.syntax] > **Syntax**\ @@ -32,7 +32,7 @@ Rust supports five loop expressions: r[expr.loop.break-label] All five types of loop support [`break` expressions](#break-expressions), and [labels](#loop-labels). -r[expr.loop.continue] +r[expr.loop.continue-label] All except labelled block expressions support [`continue` expressions](#continue-expressions). r[expr.loop.explicit-result] diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 5ae5a1f4d..463cf7721 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -305,7 +305,7 @@ r[expr.arith-logic.syntax] >    | [_Expression_] `<<` [_Expression_]\ >    | [_Expression_] `>>` [_Expression_] -r[expr.arith-logic.syntax] +r[expr.arith-logic.intro] Binary operators expressions are all written with infix notation. r[expr.arith-logic.behavior] diff --git a/src/expressions/tuple-expr.md b/src/expressions/tuple-expr.md index 2986a5601..fb007b635 100644 --- a/src/expressions/tuple-expr.md +++ b/src/expressions/tuple-expr.md @@ -4,8 +4,6 @@ r[expr.tuple] ## Tuple expressions -r[expr.tuple] - r[expr.tuple.syntax] > **Syntax**\ > _TupleExpression_ :\ From da42d7c78332b5c239616760ad954c8a14895c3d Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 11 Dec 2024 18:51:55 -0500 Subject: [PATCH 14/14] Apply suggestions from code review Co-authored-by: Travis Cross --- src/expressions.md | 2 +- src/expressions/grouped-expr.md | 2 +- src/expressions/method-call-expr.md | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index a275cd80a..d4948274c 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -197,7 +197,7 @@ Explicitly, the assignee expressions are: fields). - [Unit structs][_StructExpression_]. -r[expr.place-value.parentehesis] +r[expr.place-value.parenthesis] Arbitrary parenthesisation is permitted inside assignee expressions. ### Moved and copied types diff --git a/src/expressions/grouped-expr.md b/src/expressions/grouped-expr.md index 77d499657..1738864d6 100644 --- a/src/expressions/grouped-expr.md +++ b/src/expressions/grouped-expr.md @@ -18,7 +18,7 @@ r[expr.paren.place-or-value] 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. -r[expr.paren.overridew-precedence] +r[expr.paren.override-precedence] Parentheses can be used to explicitly modify the precedence order of subexpressions within an expression. An example of a parenthesized expression: diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 341b9c9d2..18bb218ca 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -24,11 +24,11 @@ When looking up a method call, the receiver may be automatically dereferenced or This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call. The following procedure is used: -r[expr.method.candidate-recievers] +r[expr.method.candidate-receivers] The first step is to build a list of candidate receiver types. Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion] at the end, and adding the result type if that is successful. -r[expr.method.candidate-recievers-refs] +r[expr.method.candidate-receivers-refs] Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`. For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types will be `Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion), `&[i32]`, and finally `&mut [i32]`. @@ -69,14 +69,14 @@ Then, for each candidate type `T`, search for a [visible] method with a receiver > } > ``` -r[expr.method.ambiguious-target] +r[expr.method.ambiguous-target] If this results in multiple possible candidates, then it is an error, and the receiver must be [converted][disambiguate call] to an appropriate receiver type to make the method call. r[expr.method.constraint] This process does not take into account the mutability or lifetime of the receiver, or whether a method is `unsafe`. Once a method is looked up, if it can't be called for one (or more) of those reasons, the result is a compiler error. -r[expr.method.ambiguious-search] +r[expr.method.ambiguous-search] If a step is reached where there is more than one possible method, such as where generic methods or traits are considered the same, then it is a compiler error. These cases require a [disambiguating function call syntax] for method and function invocation.