From 784b5f87ac5d90dd450d82d8c1d424b18a88550e Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:45:25 +0000 Subject: [PATCH 01/17] Fix the `Drop` condition in the movable place list --- src/expressions.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/expressions.md b/src/expressions.md index 9943384dcf..9f1bb7c1ee 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -207,7 +207,23 @@ Only the following place expressions may be moved out of: * [Variables] which are not currently borrowed. * [Temporary values](#temporaries). -* [Fields][field] of a place expression which can be moved out of and don't implement [`Drop`]. +* [Fields][field] 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`. + ``` + * The result of [dereferencing][deref] an expression with type [`Box`] and that can also be moved out of. r[expr.move.deinitialization] From 9e6aed9d59d81e9837c93fc206d7fe20435d0642 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:46:42 +0000 Subject: [PATCH 02/17] Fix grammar of the `Box` entry in the movable list --- src/expressions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/expressions.md b/src/expressions.md index 9f1bb7c1ee..bf21841f34 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -224,7 +224,7 @@ Only the following place expressions may be moved out of: let y = x.0; // ERROR: Cannot move a field out of a type that implements `Drop`. ``` -* The result of [dereferencing][deref] an expression with type [`Box`] and that can also be moved out of. +* The result of [dereferencing] an expression of type [`Box`] that can itself 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. @@ -403,6 +403,7 @@ They are never allowed before: [comparison]: expressions/operator-expr.md#comparison-operators [compound assignment]: expressions/operator-expr.md#compound-assignment-expressions [deref]: expressions/operator-expr.md#the-dereference-operator +[dereferencing]: expr.deref [destructors]: destructors.md [drop scope]: destructors.md#drop-scopes [extended]: destructors.scope.lifetime-extension From 0d111529a6f042ed2fa5069df23523c82fe04137 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:47:35 +0000 Subject: [PATCH 03/17] Define mutable place expression contexts --- src/expressions.md | 81 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index bf21841f34..d6255fe4c9 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -236,10 +236,52 @@ 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. * Mutable [variables] which are not currently borrowed. * [Mutable `static` items]. @@ -250,6 +292,26 @@ The following expressions can be mutable place expression contexts: * 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. +r[expr.mut.invalid-places] +Using any other place expression in one of the contexts listed in [expr.mut.intro] is an error. + +r[expr.mut.other-borrows] +The place that a `ref mut` binding mode borrows (see [patterns.ident.binding]) must also be a mutable place expression. That borrow is not a mutable place expression context, so it does not cause 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; +} +``` + r[expr.temporary] ### Temporaries @@ -386,23 +448,31 @@ 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 [dereferencing]: expr.deref [destructors]: destructors.md [drop scope]: destructors.md#drop-scopes @@ -413,8 +483,10 @@ 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 +[inline assembly]: asm [interior mutability]: interior-mutability.md [let statement]: statements.md#let-statements [macro invocations]: macro.invocation @@ -422,11 +494,13 @@ 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 [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 [statement]: statements.md @@ -437,6 +511,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 From 0bc15037b58496437c456ef6bbf59c8d9cf03ff3 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:48:03 +0000 Subject: [PATCH 04/17] Move the borrow-state qualifier to the borrow rules --- src/expressions.md | 10 ++++++++-- src/expressions/operator-expr.md | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index d6255fe4c9..910fc7536b 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -205,7 +205,7 @@ In the remaining situations, if that type is [`Sized`], then it may be possible 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 that can be moved out of and whose type does not implement [`Drop`]. @@ -226,6 +226,9 @@ Only the following place expressions may 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. @@ -283,7 +286,7 @@ let _ = r; r[expr.mut.valid-places] Only the following place expressions are mutable place expressions. -* Mutable [variables] which are not currently borrowed. +* Mutable [variables]. * [Mutable `static` items]. * [Temporary values]. * [Fields][field]: this evaluates the subexpression in a mutable place expression context. @@ -312,6 +315,9 @@ fn f(r: &&mut (u8,)) { } ``` +> [!NOTE] +> A mutable place expression also cannot be mutated while it is borrowed. See [expr.operator.borrow.lifetime]. + r[expr.temporary] ### Temporaries diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index c692122bf4..d894929985 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. From 273460f7d8f475058fcb266bb53d4321c50521ee Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 03:02:39 +0000 Subject: [PATCH 05/17] Allow deferred initialization in the mutable place list --- src/expressions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/expressions.md b/src/expressions.md index 910fc7536b..03c9ba86f0 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -287,6 +287,7 @@ r[expr.mut.valid-places] Only the following place expressions are mutable place expressions. * 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. From a6b41e1e08ba1b042b0e21edc985a479b175f3ee Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:49:05 +0000 Subject: [PATCH 06/17] Say how mutability passes through field access --- src/expressions.md | 2 +- src/expressions/field-expr.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 03c9ba86f0..3612ac2aaa 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -290,7 +290,7 @@ Only the following place expressions are mutable place expressions. * [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. +* [Fields][field]: mutable when the container operand, after automatic dereferencing (see [expr.field.autoref-deref]), is. * [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. diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index 2073979610..fb4451a942 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,7 +45,7 @@ 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 @@ -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 From 55dd878fd14a7f17f7394327c9ac065d922026d3 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:50:36 +0000 Subject: [PATCH 07/17] Add parenthesized expressions to the place lists --- src/expressions.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/expressions.md b/src/expressions.md index 3612ac2aaa..a5cfca8827 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -224,6 +224,7 @@ Only the following place expressions may be moved out of: let y = x.0; // ERROR: Cannot move a field out of a type that implements `Drop`. ``` +* [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] @@ -291,6 +292,7 @@ Only the following place expressions are mutable place expressions. * [Mutable `static` items]. * [Temporary values]. * [Fields][field]: mutable when the container operand, after automatic dereferencing (see [expr.field.autoref-deref]), is. +* [Parenthesized expressions]: mutable when the enclosed operand is. * [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. @@ -502,6 +504,7 @@ They are never allowed before: [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 From 6fc20ad5d4aa46e1eb875f4d55559188b5b89486 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:51:00 +0000 Subject: [PATCH 08/17] Distinguish built-in from trait indexing --- src/expressions.md | 3 ++- src/expressions/array-expr.md | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index a5cfca8827..d1aa53224d 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -296,7 +296,7 @@ Only the following place expressions are mutable place expressions. * [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. +* [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. r[expr.mut.invalid-places] Using any other place expression in one of the contexts listed in [expr.mut.intro] is an error. @@ -495,6 +495,7 @@ They are never allowed before: [implicit mutable borrow]: expr.implicit-borrow [implicitly borrowed]: expr.implicit-borrow [index expression]: expr.array.index +[indexing]: expr.array.index [inline assembly]: asm [interior mutability]: interior-mutability.md [let statement]: statements.md#let-statements 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 From 334b2b8ea5d425f75ba91db6cdf4b3bb476741ad Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:52:30 +0000 Subject: [PATCH 09/17] Say which dereferences are built in --- src/expressions.md | 5 +++-- src/expressions/operator-expr.md | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index d1aa53224d..1bcf7d8d7a 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -294,8 +294,9 @@ Only the following place expressions are mutable place expressions. * [Fields][field]: mutable when the container operand, after automatic dereferencing (see [expr.field.autoref-deref]), is. * [Parenthesized expressions]: mutable when the enclosed operand is. * [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. +* Dereference of a variable, or field of a variable, with type `&mut T`. +* Dereferences of a `Box`: mutable when the operand is. +* Dereferences that use [`DerefMut`] (see [expr.mut.operands]): mutable when the 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. r[expr.mut.invalid-places] diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index d894929985..c775d54e15 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -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; @@ -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 From cf3b11a24eb795e0ff17db06401e0a9679af3129 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 04:30:32 +0000 Subject: [PATCH 10/17] Fix the `&mut T` exception in the mutable place list --- src/expressions.md | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 1bcf7d8d7a..0f5d280cf9 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -285,7 +285,7 @@ let _ = r; ``` r[expr.mut.valid-places] -Only the following place expressions are mutable place expressions. +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]. * [Variables] not yet initialized on any control flow path reaching the context that initializes them (see [variable.init]). @@ -293,11 +293,42 @@ Only the following place expressions are mutable place expressions. * [Temporary values]. * [Fields][field]: mutable when the container operand, after automatic dereferencing (see [expr.field.autoref-deref]), is. * [Parenthesized expressions]: mutable when the enclosed operand is. -* [Dereferences][deref] of a `*mut T` pointer. -* Dereference of a variable, or field of a variable, with type `&mut T`. +* [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. +* [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. -* [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. +* 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. @@ -483,6 +514,7 @@ They are never allowed before: [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 @@ -515,6 +547,7 @@ They are never allowed before: [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 From d8c75f60b1dade1f8a661eab4299492bfa3cf9f3 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:53:18 +0000 Subject: [PATCH 11/17] Say that `&T` and `*const T` dereferences are immutable --- src/expressions.md | 3 +++ src/types/pointer.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/expressions.md b/src/expressions.md index 0f5d280cf9..f6b453aa2d 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -333,6 +333,9 @@ Only the following place expressions are mutable place expressions. Most entries 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]) must also be a mutable place expression. That borrow is not a mutable place expression context, so it does not cause an index or dereference within the place to use `IndexMut` or `DerefMut` (see [expr.mut.operands]). 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. From 523a542fb91915015dfec8a96a077c4b3a043493 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:54:06 +0000 Subject: [PATCH 12/17] Add array destructuring to the place lists --- src/expressions.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/expressions.md b/src/expressions.md index f6b453aa2d..8ec492cf61 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -224,6 +224,18 @@ Only the following place expressions may be moved out of: 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. @@ -294,6 +306,7 @@ Only the following place expressions are mutable place expressions. Most entries * [Fields][field]: mutable when the container operand, 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. From bf751e03db99f75c01605de2fc1ce92f5a3e2531 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:54:06 +0000 Subject: [PATCH 13/17] Say "variable" where parameters are also meant --- src/expressions.md | 4 ++-- src/expressions/field-expr.md | 2 +- src/expressions/operator-expr.md | 2 +- src/expressions/path-expr.md | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 8ec492cf61..eb6e283708 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -152,7 +152,7 @@ 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] which refer to [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. @@ -243,7 +243,7 @@ Only the following place expressions may be moved out of: > 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. diff --git a/src/expressions/field-expr.md b/src/expressions/field-expr.md index fb4451a942..a1ade1bb0c 100644 --- a/src/expressions/field-expr.md +++ b/src/expressions/field-expr.md @@ -50,7 +50,7 @@ If the type of the container operand implements [`Deref`], it is *automatically 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 } diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index c775d54e15..57d742e519 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -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. 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 From da9168f4ac96859fb014a8fd907768d4b37b4d6b Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:54:06 +0000 Subject: [PATCH 14/17] Account for tuple indexing among the place expressions --- src/expressions.md | 6 +++--- src/expressions/tuple-expr.md | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index eb6e283708..b7d9a71786 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -152,7 +152,7 @@ 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 [variables], [static variables], [dereferences][deref] (`*expr`), [array indexing] expressions (`expr[expr]`), [field] references (`expr.f`) and parenthesized place expressions. +These expressions are [paths] which 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. @@ -207,7 +207,7 @@ Only the following place expressions may be moved out of: * [Variables]. * [Temporary values](#temporaries). -* [Fields][field] of a place expression that can be moved out of and whose type does not implement [`Drop`]. +* 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; @@ -303,7 +303,7 @@ Only the following place expressions are mutable place expressions. Most entries * [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]: mutable when the container operand, after automatic dereferencing (see [expr.field.autoref-deref]), is. +* 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. 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 From 2a59be0f1bc0bcf4cf9e36d1e70894570679c093 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 05:23:16 +0000 Subject: [PATCH 15/17] Say that a `let` with `ref` is not a coercion site --- src/type-coercions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/type-coercions.md b/src/type-coercions.md index 76b994d64c..755efb1f30 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). From 98b81709d60c0d3db9757e2ff387586c8e219386 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 00:54:24 +0000 Subject: [PATCH 16/17] Document the reborrow at coercion sites --- src/expressions.md | 22 +++++++++++++-- src/type-coercions.md | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index b7d9a71786..a87d16dc73 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -202,6 +202,9 @@ 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: @@ -246,7 +249,7 @@ r[expr.move.deinitialization] 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 @@ -350,7 +353,7 @@ Using any other place expression in one of the contexts listed in [expr.mut.intr > 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]) must also be a mutable place expression. That borrow is not a mutable place expression context, so it does not cause an index or dereference within the place to use `IndexMut` or `DerefMut` (see [expr.mut.operands]). +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,)) { @@ -366,6 +369,20 @@ fn f(r: &&mut (u8,)) { } ``` +```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]. @@ -482,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 diff --git a/src/type-coercions.md b/src/type-coercions.md index 755efb1f30..fcaa1a0757 100644 --- a/src/type-coercions.md +++ b/src/type-coercions.md @@ -213,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 @@ -301,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 From a07a98c227544bdacf577a493a680533d5b5972e Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 10 Oct 2026 03:02:39 +0000 Subject: [PATCH 17/17] Fix spelling and punctuation in place expression rules --- src/expressions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index a87d16dc73..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 [variables] or [static variables], [dereferences] (`*expr`), [array indexing] expressions (`expr[expr]`), [field] references (`expr.f`), [tuple indexing] expressions (`expr.0`), 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