diff --git a/src/expressions.md b/src/expressions.md index 9943384dcf..6401acc15a 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -146,13 +146,13 @@ r[expr.place-value] ## Place expressions and value expressions 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. +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 on both 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. +These expressions are [paths] that refer to [variables] or [static variables], [dereferences] (`*expr`), [array indexing] expressions (`expr[expr]`), [field] references (`expr.f`), [tuple indexing] expressions (`expr.0`), and parenthesized place expressions. r[expr.place-value.value-expr-kinds] All other expressions are value expressions. @@ -164,7 +164,7 @@ r[expr.place-value.place-context] The following contexts are *place expression* contexts: * The left operand of a [compound assignment] expression. -* The operand of a unary [borrow], [raw borrow] or [dereference][deref] operator. +* The operand of a unary [borrow], [raw borrow], or [dereference] operator. * The operand of a [field expression]. * The indexed operand of an [array indexing expression]. * The tuple operand of a [tuple indexing expression]. @@ -185,10 +185,10 @@ An *assignee expression* is an expression that appears in the left operand of an - [Slices][expr.array.index] of assignee expressions. - [Tuple structs] of assignee expressions. - [Structs] of assignee expressions (with optionally named fields). -- [Unit structs] +- [Unit structs]. r[expr.place-value.parenthesis] -Arbitrary parenthesisation is permitted inside assignee expressions. +Arbitrary parenthesization is permitted inside assignee expressions. r[expr.move] ### Moved and copied types @@ -202,37 +202,189 @@ 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. +> [!NOTE] +> A `&mut T` coerced to a type known to be a reference or raw pointer type is reborrowed rather than moved. See [coerce.reborrow]. + r[expr.move.movable-place] Only the following place expressions may be moved out of: -* [Variables] which are not currently borrowed. +* [Variables]. * [Temporary values](#temporaries). -* [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. +* Fields of a struct, union, tuple, or enum variant, whether accessed by a [field] or [tuple indexing] expression or matched by a pattern, of a place expression that can be moved out of and whose type does not implement [`Drop`]. + + ```rust + struct D; + impl Drop for D { fn drop(&mut self) {} } + struct S(D); + let x = S(D); + let y = x.0; // OK: `S` does not implement `Drop`, though `D` does. + ``` + + ```rust,compile_fail,E0509 + struct S(String); + impl Drop for S { fn drop(&mut self) {} } + let x = S(String::new()); + let y = x.0; // ERROR: Cannot move a field out of a type that implements `Drop`. + ``` + +* Elements or subarrays of an array that can be moved out of, when bound by value in a [slice pattern]. + + ```rust + let x = [String::new(), String::new()]; + let [a, _] = x; // OK: Moves the first element out of `x`. + ``` + + ```rust,compile_fail,E0508 + let x = [String::new()]; + let a = x[0]; // ERROR: An index expression cannot be moved out of. + ``` + +* [Parenthesized expressions] whose enclosed operand can be moved out of. +* The result of [dereferencing] an expression of type [`Box`] that can itself be moved out of. + +> [!NOTE] +> A place expression cannot be moved out of while it is borrowed. See [expr.operator.borrow.lifetime]. 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. +After moving out of a place expression that evaluates to a 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. +In all other cases, trying to use a place expression in a value expression context is an error, unless the expression is reborrowed (see [coerce.reborrow]). r[expr.mut] ### Mutability 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*. +The following contexts are *mutable place expression contexts*: + +* The left operand of an [assignment] or [compound assignment] expression and, in a destructuring assignment, each place expression it assigns to (see [expr.assign.destructure]). +* The operand of a mutable [borrow] expression (`&mut`) or a mutable [raw borrow] expression (`&raw mut`). +* The operand of an [implicit mutable borrow]. +* The output operands of an [inline assembly] expression. +* The place matched by a [`ref mut` binding] whose `ref mut` is written in the pattern. + +The *mutable place expressions*, the place expressions that may be used in these contexts, are listed in [expr.mut.valid-places]. All other place expressions are *immutable place expressions*. + +> [!NOTE] +> A `ref mut` [binding mode] that arises from matching a reference with a non-reference pattern does not put the scrutinee in a mutable place expression context, so it does not cause an index or dereference within the scrutinee to use `IndexMut` or `DerefMut`. This behavior is settled: [rust-lang/rust#44848], which tracked it, was closed with the current handling judged adequate and stabilized. The place that the binding borrows must nonetheless be a mutable place expression (see [expr.mut.other-borrows]). +> +> ```rust,compile_fail,E0596 +> # let mut a = (0u8,); +> let mut v: Vec> = vec![Some(&mut a)]; +> if let Some((x,)) = v[0] { *x = 1; } // ERROR: `v[0]` uses `Index`, so the field that `x` borrows is not mutable. +> ``` +> +> ```rust +> # let mut a = (0u8,); +> let mut v: Vec> = vec![Some(&mut a)]; +> if let Some(&mut (ref mut x,)) = v[0] { *x = 1; } // OK: The written `ref mut` makes `v[0]` use `IndexMut`. +> ``` + +r[expr.mut.operands] +When a place expression is in a mutable place expression context, so is each place expression it is built on, after automatic dereferencing: the enclosed operand of a [parenthesized expression], the container operand of a [field] or [tuple indexing] expression, the indexed operand of an [index expression], the operand of a [dereference], whether written or automatic, and the scrutinee that a pattern destructures. In such a context, an index expression that is not built in (see [expr.array.index.array]) uses [`IndexMut`] when its operand's type implements `IndexMut` for the index type of the [`Index`] implementation it uses, and `Index` otherwise; a dereference, written or automatic, of a type other than a reference, raw pointer, or `Box` (see [expr.deref.traits]) uses [`DerefMut`] when that type implements it, and [`Deref`] otherwise. The implicit mutable borrow that `IndexMut` and `DerefMut` take is a mutable place expression context for their operand. + +```rust +# use std::rc::Rc; +# let mut a = 0u8; +struct P { p: *mut u8 } +let rc = Rc::new(P { p: &mut a }); +unsafe { *rc.p = 1; } // OK: `Rc` does not implement `DerefMut`, so `*rc` uses `Deref`, and `*rc.p` is mutable because `rc.p` is a `*mut u8`. +``` + +```rust,compile_fail,E0502 +# let mut a = 0u8; +let mut v: Vec<&mut u8> = vec![&mut a]; +let r = &v; +*v[0] = 1; // ERROR: `v[0]` uses `IndexMut`, which mutably borrows `v` while `r` borrows it. +let _ = r; +``` r[expr.mut.valid-places] -The following expressions can be mutable place expression contexts: +Only the following place expressions are mutable place expressions. Most entries make a place expression mutable exactly when the operand it is built on is; the dereference of a `*mut T` is mutable whatever its operand, and so, under the conditions of its entry, is the dereference of a `&mut T`. -* Mutable [variables] which are not currently borrowed. +* Mutable [variables]. +* [Variables] not yet initialized on any control flow path reaching the context that initializes them (see [variable.init]). * [Mutable `static` items]. * [Temporary values]. -* [Fields][field]: this evaluates the subexpression in a mutable place expression context. -* [Dereferences][deref] of a `*mut T` pointer. -* Dereference of a variable, or field of a variable, with type `&mut T`. Note: This is an exception to the requirement of the next rule. -* Dereferences of a type that implements `DerefMut`: this then requires that the value being dereferenced is evaluated in a mutable place expression context. -* [Array indexing] of a type that implements `IndexMut`: this then evaluates the value being indexed, but not the index, in mutable place expression context. +* Fields of a struct, union, tuple, or enum variant, whether accessed by a [field] or [tuple indexing] expression or matched by a pattern: mutable when the place expression that holds the struct, union, tuple, or enum, after automatic dereferencing (see [expr.field.autoref-deref]), is. +* [Parenthesized expressions]: mutable when the enclosed operand is. +* [Indexing] of an array or slice by a value of type `usize`, or indexing that uses [`IndexMut`] (see [expr.mut.operands]): mutable when the indexed operand, after automatic dereferencing (see [expr.array.index.autoderef]), is. +* Elements or subslices of an array or slice matched by a [slice pattern]: mutable when the array or slice is. +* [Dereferences] of a `*mut T` pointer. +* Dereferences of a `Box`: mutable when the operand is. +* Dereferences that use [`DerefMut`] (see [expr.mut.operands]): mutable when the operand is. +* Dereferences of a place expression of type `&mut T`, where that place expression is a mutable place expression, is a variable, or is obtained from one of those by any sequence of the following operations: + * Parenthesization. + * Dereferencing, by an expression or by a pattern, of a place of type `&mut U` or `Box`. + * Access to a field, by a [field] or [tuple indexing] expression or by a pattern, or to an element or subslice of an array or slice, by indexing with a value of type `usize` or by a [slice pattern], in either case possibly after automatic dereferencing where that dereferencing is of an expression of type `&mut U` or `Box`. + + ```rust + struct S<'a>(&'a mut u8); + fn f(x: &mut u8, q: &mut &mut u8, s: S<'_>, b: Box<&mut u8>, c: Box>, a: [&mut u8; 1], o: Option<&mut S<'_>>) { + *x = 1; // OK: `x` is a variable, though not declared `mut`. + **q = 1; // OK: `*q` is a dereference of a `&mut` held by a variable. + *s.0 = 1; // OK: `s.0` is a field of a variable. + **b = 1; // OK: `*b` is a dereference of a `Box` held by a variable. + *c.0 = 1; // OK: `c.0` is a field of the dereference of a `Box` held by a variable. + *a[0] = 1; // OK: `a[0]` is an element of an array held by a variable. + if let Some(S(y)) = o { **y = 1; } // OK: The `ref mut` binding mode borrows a field reached through a `&mut` held by a variable. + } + ``` + + ```rust,compile_fail,E0594 + fn f(r: &&mut u8) { + **r = 1; // ERROR: `*r` is reached through a shared reference. + } + ``` + + ```rust,compile_fail,E0596 + fn f(v: Vec<&mut u8>) { + *v[0] = 1; // ERROR: `v[0]` indexes through `IndexMut`, which mutably borrows `v`, so `v` must be mutable. + } + ``` + +> [!NOTE] +> The `mut` on a variable governs assignment to and mutable borrows of the variable and the places it owns, such as its fields and the contents of a `Box` it holds. A mutable reference is the only way to access its referent (see [type.pointer.reference.mut.copy]), so a `&mut T` can be written through whether or not the variable holding it is declared `mut`. The contents of a `Box` take the mutability of the place that holds the box, but a `&mut U` among those contents is still the only way to access its own referent. Indexing or dereferencing through [`IndexMut`] or [`DerefMut`] mutably borrows the operand (see [expr.mut.operands]), so the operand must itself be mutable even when the place reached through it is the referent of a `&mut`. Indexing an array or slice by a `usize` and dereferencing a `&mut` reference or a `Box` take no such borrow, so they may lie on the path from a variable to a `&mut T` without any `mut` being required. + +r[expr.mut.invalid-places] +Using any other place expression in one of the contexts listed in [expr.mut.intro] is an error. + +> [!NOTE] +> In particular, the dereference of a shared reference or of a `*const T` pointer is never a mutable place expression, and neither is a field, element, or dereference reached through one unless a dereference of a `*mut T` pointer intervenes. + +r[expr.mut.other-borrows] +The place that a `ref mut` binding mode borrows (see [patterns.ident.binding]), and the place `*e` that a coercion of a `&mut T` to a `*mut U` borrows (see [coerce.reborrow]), must also be mutable place expressions. Neither borrow is a mutable place expression context, so neither causes an index or dereference within the place to use `IndexMut` or `DerefMut` (see [expr.mut.operands]). + +```rust +fn f(r: &mut &mut (u8,)) { + let (x,) = *r; // OK: The `ref mut` binding mode borrows `(**r).0`, which is mutable. + *x = 1; +} +``` + +```rust,compile_fail,E0596 +fn f(r: &&mut (u8,)) { + let (x,) = *r; // ERROR: The `ref mut` binding mode borrows `(**r).0`, which is reached through a shared reference. + *x = 1; +} +``` + +```rust +fn f(r: &mut &mut u8) -> *mut u8 { + let p: *mut u8 = *r; // OK: The coercion borrows `**r`, which is mutable. + p +} +``` + +```rust,compile_fail,E0596 +fn f(r: &&mut u8) -> *mut u8 { + let p: *mut u8 = *r; // ERROR: The coercion borrows `**r`, which is reached through a shared reference. + p +} +``` + +> [!NOTE] +> A mutable place expression also cannot be mutated while it is borrowed. See [expr.operator.borrow.lifetime]. r[expr.temporary] ### Temporaries @@ -347,6 +499,7 @@ Implicit borrows may be taken in the following expressions: * Operands of [comparison]. * Left operands of the [compound assignment]. * Arguments to [`format_args!`] except the format string. +* The referent of an expression of type `&mut T` reborrowed by a coercion to a reference type (see [coerce.reborrow]). r[expr.overload] ## Overloading traits @@ -370,23 +523,33 @@ They are never allowed before: * Binary operator expressions ([ArithmeticOrLogicalExpression], [ComparisonExpression], [LazyBooleanExpression], [TypeCastExpression], [AssignmentExpression], [CompoundAssignmentExpression]). [`Box`]: special-types-and-traits.md#boxt +[binding mode]: patterns.ident.binding [`Copy`]: special-types-and-traits.md#copy +[`Deref`]: lang-types.deref +[`DerefMut`]: lang-types.deref [`Drop`]: special-types-and-traits.md#drop [`if let`]: expressions/if-expr.md#if-let-patterns +[`Index`]: std::ops::Index +[`IndexMut`]: std::ops::IndexMut [`format_args!`]: core::format_args [`pin!`]: core::pin::pin +[`ref mut` binding]: patterns.ident.ref [`Sized`]: special-types-and-traits.md#sized [`while let`]: expressions/loop-expr.md#while-let-patterns [array expressions]: expressions/array-expr.md [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions [array indexing expression]: expr.array.index [assign]: expressions/operator-expr.md#assignment-expressions +[assignment]: expr.assign [block expressions]: expressions/block-expr.md [borrow]: expressions/operator-expr.md#borrow-operators [call expressions]: expressions/call-expr.md [comparison]: expressions/operator-expr.md#comparison-operators [compound assignment]: expressions/operator-expr.md#compound-assignment-expressions [deref]: expressions/operator-expr.md#the-dereference-operator +[dereference]: expr.deref +[dereferences]: expr.deref +[dereferencing]: expr.deref [destructors]: destructors.md [drop scope]: destructors.md#drop-scopes [extended]: destructors.scope.lifetime-extension @@ -396,8 +559,11 @@ They are never allowed before: [field expression]: expr.field [functional update]: expressions/struct-expr.md#functional-update-syntax [implicit borrow]: #implicit-borrows +[implicit mutable borrow]: expr.implicit-borrow [implicitly borrowed]: expr.implicit-borrow -[implicitly mutably borrowed]: #implicit-borrows +[index expression]: expr.array.index +[indexing]: expr.array.index +[inline assembly]: asm [interior mutability]: interior-mutability.md [let statement]: statements.md#let-statements [macro invocations]: macro.invocation @@ -405,13 +571,17 @@ They are never allowed before: [method-call]: expressions/method-call-expr.md [Mutable `static` items]: items/static-items.md#mutable-statics [Outer attributes]: attributes.md +[parenthesized expression]: expr.paren +[parenthesized expressions]: expr.paren [paths]: expressions/path-expr.md [place expression contexts]: expr.place-value [promoted]: destructors.md#constant-promotion [Range]: expressions/range-expr.md [raw borrow]: expressions/operator-expr.md#raw-borrow-operators +[rust-lang/rust#44848]: https://github.com/rust-lang/rust/issues/44848 [scrutinee]: glossary.md#scrutinee [slice]: types/slice.md +[slice pattern]: patterns.slice [statement]: statements.md [static variables]: items/static-items.md [struct]: expressions/struct-expr.md @@ -420,6 +590,7 @@ They are never allowed before: [temporary scopes]: destructors.scope.temporary [Temporary values]: #temporaries [tuple expressions]: expressions/tuple-expr.md +[tuple indexing]: expr.tuple-index [tuple indexing expression]: expr.tuple-index [Tuple structs]: items.struct.tuple [Tuples]: expressions/tuple-expr.md diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index d0fe247635..e5505e3c8a 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -80,11 +80,14 @@ r[expr.array.index.syntax] IndexExpression -> Expression `[` Expression `]` ``` +r[expr.array.index.autoderef] +The indexed operand `a` is automatically dereferenced as many times as necessary to find an array or slice type or a type that implements [`Index`] or [`IndexMut`], as the receiver of a method call is (see [expr.method.autoref-deref]). The following rules, and the rules on [place expressions], apply to the dereferenced operand. + 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. +An index expression `a[b]` where `a` has an [array] or [slice] type and `b` has type `usize` is built in and evaluates to the location of the element of `a` at index `b`. The element can be assigned to when the index expression is a mutable place expression (see [expr.mut.valid-places]). 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, except that when the index expression undergoes [temporary lifetime extension], the indexed expression `a` also has its [temporary scope] extended. Just as with methods, Rust will also insert dereference operations on `a` repeatedly to find an implementation. +Otherwise, 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] when the type of `a` also implements `IndexMut` for the index type of that `Index` implementation, except that when the index expression undergoes [temporary lifetime extension], the indexed expression `a` also has its [temporary scope] extended. ```rust // The temporary holding the result of `vec![()]` is extended to @@ -129,6 +132,8 @@ 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 +[`Index`]: std::ops::Index +[`IndexMut`]: std::ops::IndexMut [IndexMut]: std::ops::IndexMut [Index]: std::ops::Index [array]: ../types/array.md @@ -138,9 +143,10 @@ The array index expression can be implemented for types other than arrays and sl [constant item]: ../items/constant-items.md [inferred const]: items.generics.const.inferred [literal]: ../tokens.md#literals -[memory location]: ../expressions.md#place-expressions-and-value-expressions +[mutable place expression context]: expr.mut.intro [panic]: ../panic.md [path]: path-expr.md +[place expressions]: expr.place-value [slice]: ../types/slice.md [temporary lifetime extension]: destructors.scope.lifetime-extension [temporary scope]: destructors.scope.temporary diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index 2073979610..a1ade1bb0c 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -10,7 +10,7 @@ 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. +A field expression can be assigned to or mutably borrowed when it is a [mutable] place expression (see [expr.mut.valid-places]). r[expr.field.form] The syntax for a field expression is an expression, called the *container operand*, then a `.`, and finally an [identifier]. @@ -45,12 +45,12 @@ foo().x; r[expr.field.autoref-deref] ## Automatic dereferencing -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. +If the type of the container operand implements [`Deref`], it is *automatically dereferenced* as many times as necessary to make the field access possible; when the field expression is in a [mutable place expression context], each of those dereferences is evaluated in one too (see [expr.deref.traits]). This process is also called *autoderef* for short. For the rules on [place expressions], such as which of them are [mutable], the field access applies to the dereferenced operand. r[expr.field.borrow] ## Borrowing -The fields of a struct or a reference to a struct are treated as separate entities when borrowing. If the struct does not implement [`Drop`] and is stored in a local variable, this also applies to moving out of each of its fields. This also does not apply if automatic dereferencing is done through user-defined types other than [`Box`]. +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 variable, this also applies to moving out of each of its fields. This also does not apply if automatic dereferencing is done through user-defined types other than [`Box`]. ```rust struct A { f1: String, f2: String, f3: String } @@ -73,7 +73,9 @@ let d: String = x.f3; // Move out of x.f3 [call expression]: call-expr.md [method call expression]: method-call-expr.md [mutable]: ../expressions.md#mutability +[mutable place expression context]: expr.mut.intro [parenthesized expression]: grouped-expr.md [place expression]: ../expressions.md#place-expressions-and-value-expressions +[place expressions]: expr.place-value [struct]: ../items/structs.md [union]: ../items/unions.md diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index c692122bf4..57d742e519 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -68,7 +68,7 @@ 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. +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 or moved out of, 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. @@ -164,10 +164,10 @@ 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) or [`Box`], it denotes the pointed-to location. +When applied to a [reference], [raw pointer], or [`Box`], it denotes the pointed-to location. r[expr.deref.mut] -If the expression is of type `&mut T`, `*mut T`, or `Box`, 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. +The resulting memory location can be assigned to when the dereference expression is a mutable [place expression] (see [expr.mut.valid-places]). r[expr.deref.box] When applied to a [`Box`], the resultant place may be [moved from]. @@ -176,7 +176,7 @@ 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, except that when `*x` undergoes [temporary lifetime extension], the dereferenced expression `x` also has its [temporary scope] extended. +On types other than references, raw pointers, and [`Box`], `*x` is equivalent to `*std::ops::Deref::deref(&x)`, or to `*std::ops::DerefMut::deref_mut(&mut x)` in a [mutable place expression context] when the type of `x` implements `DerefMut`, except that when `*x` undergoes [temporary lifetime extension], the dereferenced expression `x` also has its [temporary scope] extended. ```rust # struct NoCopy; @@ -933,7 +933,7 @@ For destructuring assignment, subexpressions of the assignee expression are eval > 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. +It then has the effect of first [dropping] the value at the assigned place, unless the place is an uninitialized variable or an uninitialized field of a variable. r[expr.assign.behavior] Next it either [copies or moves] the assigned value to the assigned place. @@ -1247,7 +1247,10 @@ As with normal assignment expressions, compound assignment expressions always pr [metadata]: dynamic-sized.pointer-types [moved from]: expr.move.movable-place [mutable]: ../expressions.md#mutability +[mutable place expression context]: expr.mut.intro [place expression]: ../expressions.md#place-expressions-and-value-expressions +[raw pointer]: type.pointer.raw +[reference]: type.pointer.reference [assignee expression]: ../expressions.md#place-expressions-and-value-expressions [undefined behavior]: ../behavior-considered-undefined.md [unit]: ../types/tuple.md diff --git a/src/expressions/path-expr.md b/src/expressions/path-expr.md index fff9b9effa..b552afca34 100644 --- a/src/expressions/path-expr.md +++ b/src/expressions/path-expr.md @@ -9,10 +9,10 @@ PathExpression -> ``` r[expr.path.intro] -A [path] used as an expression context denotes either a local variable or an item. +A [path] used in an expression context denotes either a 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]. +Path expressions that resolve to [variables] or [static variables] are [place expressions]; other paths are [value expressions]. r[expr.path.safety] Using a [`static mut`] variable requires an [`unsafe` block]. @@ -35,7 +35,9 @@ r[expr.path.const] Evaluation of associated constants is handled the same way as [`const` blocks]. [place expressions]: ../expressions.md#place-expressions-and-value-expressions +[static variables]: items.static [value expressions]: ../expressions.md#place-expressions-and-value-expressions +[variables]: variable [path]: ../paths.md [`static mut`]: ../items/static-items.md#mutable-statics [`unsafe` block]: block-expr.md#unsafe-blocks diff --git a/src/expressions/tuple-expr.md b/src/expressions/tuple-expr.md index f96dfa5cce..b9ef266583 100644 --- a/src/expressions/tuple-expr.md +++ b/src/expressions/tuple-expr.md @@ -56,6 +56,16 @@ The syntax for a tuple index expression is an expression, called the *tuple oper r[expr.tuple-index.index-syntax] 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.autoderef] +The tuple operand is automatically dereferenced as the container operand of a [field expression] is (see [expr.field.autoref-deref]). The following rules apply to the dereferenced operand. + +```rust +let mut pair = (0u8, 0u8); +let p: &mut (u8, u8) = &mut pair; +p.0 = 1; // Assigns to `(*p).0`. +assert_eq!(pair, (1, 0)); +``` + r[expr.tuple-index.required-type] The type of the tuple operand must be a [tuple type] or a [tuple struct]. @@ -89,6 +99,7 @@ assert_eq!(point.1, 0.0); [call expression]: ./call-expr.md [decimal literal]: ../tokens.md#integer-literals [field access expressions]: ./field-expr.html#field-access-expressions +[field expression]: expr.field [operands]: ../expressions.md [parenthetical expression]: grouped-expr.md [place expression]: ../expressions.md#place-expressions-and-value-expressions diff --git a/src/type-coercions.md b/src/type-coercions.md index 76b994d64c..fcaa1a0757 100644 --- a/src/type-coercions.md +++ b/src/type-coercions.md @@ -16,7 +16,7 @@ r[coerce.site.intro] A coercion can only occur at certain coercion sites in a program; these are typically places where the desired type is explicit or can be derived by propagation from explicit types (without type inference). Possible coercion sites are: r[coerce.site.let] -* `let` statements where an explicit type is given. +* `let` statements where an explicit type is given, unless `ref` or `ref mut` is written in the pattern. For example, `&mut 42` is coerced to have type `&i8` in the following: @@ -24,6 +24,10 @@ r[coerce.site.let] let _: &i8 = &mut 42; ``` + ```rust,compile_fail,E0308 + let ref x: &i8 = &mut 42; // ERROR: `ref` is written in the pattern, so `&mut 42` is not coerced to `&i8`. + ``` + r[coerce.site.value] * `static` and `const` item declarations (similar to `let` statements). @@ -209,6 +213,70 @@ Additionally, a type `Foo` can implement `CoerceUnsized>` when `T` imp > [!NOTE] > While the definition of the unsized coercions and their implementation has been stabilized, the traits themselves are not yet stable and therefore can't be used directly in stable Rust. +r[coerce.reborrow] +## Reborrowing + +r[coerce.reborrow.intro] +When an expression `e` of type `&mut T` is coerced to a type that is known, when the coercion is checked, to be a reference type or a raw pointer type, including `&mut T` itself, `e` is not moved. Instead, the referent is *reborrowed*: the coercion creates a new reference or raw pointer to `*e`, or to a place reached from `*e` by the coercion. For a reference type, this is an [implicit borrow] of `*e`, mutable when the type is a `&mut` reference, and the rules for borrows apply to it (see [expr.operator.borrow.lifetime]). + +```rust +let mut x = 0; +let r = &mut x; +let s: &mut i32 = r; // Reborrows `*r` rather than moving `r`. +*s += 1; +*r += 1; // OK: `r` was not moved. +``` + +```rust,compile_fail,E0503 +let mut x = 0; +let r = &mut x; +let s: &mut i32 = r; // Reborrows `*r`. +*r += 1; // ERROR: `*r` is borrowed by `s`. +*s += 1; +``` + +```rust,compile_fail,E0382 +let mut x = 0; +let r = &mut x; +let s = r; // Moves `r`, since a `let` statement without a type is not a coercion site. +*s += 1; +*r += 1; // ERROR: Use of moved value. +``` + +```rust +# let mut x = 0; +# let r = &mut x; +fn f(_: &mut i32) {} +f(r); // Reborrows `*r`. +f(r); // OK: `r` was not moved. +``` + +```rust,compile_fail,E0382 +# let mut x = 0; +# let r = &mut x; +fn g(_: T) {} +g(r); // Moves `r`: the type of `r` determines `T`, so `r` is not coerced to a reference type. +g(r); // ERROR: Use of moved value. +``` + +> [!NOTE] +> A coercion to a raw pointer type takes a [raw borrow] of `*e` but does not evaluate `*e` in a mutable place expression context, so it does not cause an index or dereference within `*e` to use `IndexMut` or `DerefMut`. This may change in the future. For a `*mut U` target, `*e` must nonetheless be a mutable place expression (see [expr.mut.other-borrows]). +> +> ```rust,compile_fail,E0596 +> # let mut a = 0u8; +> let mut v: Vec<&mut u8> = vec![&mut a]; +> let p: *mut u8 = v[0]; // ERROR: `v[0]` uses `Index`, so `*v[0]` is not mutable. +> ``` +> +> ```rust +> # let mut a = 0u8; +> let mut v: Vec<&mut u8> = vec![&mut a]; +> let p: *mut u8 = &raw mut *v[0]; // OK: `&raw mut` evaluates `*v[0]` in a mutable place expression context, so `v[0]` uses `IndexMut`. +> ``` + +> [!NOTE] +> A `let` statement without a type is not a coercion site (see [coerce.site]), so `let s = r;` moves `r`. In `g(r)` for `fn g(_: T)`, nothing is known about `T` when the coercion is checked, so `r` is moved and its type determines `T`; in `g::<&mut i32>(r)`, `r` is reborrowed. Likewise, for `r1` and `r2` of type `&mut i32`, in `let a = [r1, r2];` nothing else determines the array's type, so `r1` is moved and determines the element type, and `r2` is coerced to that type and reborrowed. A cast also performs a coercion (see [expr.as.coercions]), so `r as &mut i32` reborrows `*r`. In `let s = if c { r1 } else { r2 };`, the least upper bound coercion applies to the blocks, whose tails are not coercion sites when the `if` has no known type, so both `r1` and `r2` are moved. + r[coerce.least-upper-bound] ## Least upper bound coercions @@ -297,6 +365,8 @@ This description is obviously informal. Making it more precise is expected to pr [type cast operator]: expressions/operator-expr.md#type-cast-expressions [`Unsize`]: std::marker::Unsize [`CoerceUnsized`]: std::ops::CoerceUnsized +[implicit borrow]: expr.implicit-borrow +[raw borrow]: expr.borrow.raw [labeled block expression]: expr.loop.block-labels [`loop` expression with break expressions]: expr.loop.break-value [method-call expressions]: expressions/method-call-expr.md diff --git a/src/types/pointer.md b/src/types/pointer.md index ffd234a3b7..bfc88bb627 100644 --- a/src/types/pointer.md +++ b/src/types/pointer.md @@ -44,6 +44,9 @@ RawPointerType -> `*` ( `mut` | `const` ) TypeNoBounds r[type.pointer.raw.intro] Raw pointers are pointers without safety or liveness guarantees. Raw pointers are written as `*const T` or `*mut T`. For example `*const i32` means a raw pointer to a 32-bit integer. +> [!NOTE] +> The dereference of a `*mut T` is a mutable place expression, so it can be assigned to or mutably borrowed, while the dereference of a `*const T` is an immutable place expression. See [expr.mut.valid-places]. + r[type.pointer.raw.copy] Copying or dropping a raw pointer has no effect on the lifecycle of any other value.