Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 190 additions & 19 deletions src/expressions.md

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions src/expressions/array-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
8 changes: 5 additions & 3 deletions src/expressions/field-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -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 }
Expand All @@ -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
13 changes: 8 additions & 5 deletions src/expressions/operator-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<T>`, and is either a local variable, a (nested) field of a local variable or is a mutable [place expression], then the resulting memory location can be assigned to.
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].
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/expressions/path-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/expressions/tuple-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].

Expand Down Expand Up @@ -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
Expand Down
72 changes: 71 additions & 1 deletion src/type-coercions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ 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:

```rust
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).

Expand Down Expand Up @@ -209,6 +213,70 @@ Additionally, a type `Foo<T>` can implement `CoerceUnsized<Foo<U>>` 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>(_: 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>(_: 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

Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/types/pointer.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Loading