Skip to content

Commit f87e527

Browse files
committed
refactor unsizing coercion documentation
The old one was quite confusing and also incorrect in a few places.
1 parent 3bf3402 commit f87e527

File tree

2 files changed

+89
-43
lines changed

2 files changed

+89
-43
lines changed

src/expressions/method-call-expr.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The following procedure is used:
2525

2626
r[expr.method.candidate-receivers]
2727
The first step is to build a list of candidate receiver types.
28-
Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion] at the end, and adding the result type if that is successful.
28+
Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion][coerce.unsize] at the end, and adding the result type if that is successful.
2929

3030
r[expr.method.candidate-receivers-refs]
3131
Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`.
@@ -100,5 +100,4 @@ r[expr.method.edition2021]
100100
[disambiguating function call syntax]: call-expr.md#disambiguating-function-calls
101101
[dereference]: operator-expr.md#the-dereference-operator
102102
[methods]: ../items/associated-items.md#methods
103-
[unsized coercion]: ../type-coercions.md#unsized-coercions
104103
[`IntoIterator`]: std::iter::IntoIterator

src/type-coercions.md

Lines changed: 88 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ r[coerce.types.ref-to-pointer]
134134
r[coerce.types.mut-to-pointer]
135135
* `&mut T` to `*mut T`
136136

137+
r[coerce.types.unsize]
138+
* `T` to `U` if `T: CoerceUnsized<U>`. For example:
139+
```rust
140+
const _: &dyn std::fmt::Display = &0u8; // &u8 -> &dyn Display
141+
const _: &[u32] = &[0, 1, 2, 3, 4, 5]; // &[u32; 4] -> &[u32]
142+
```
143+
144+
See [unsized coercion](#unsized-coercions) for more details.
145+
137146
r[coerce.types.deref]
138147
* `&T` or `&mut T` to `&U` if `T` implements `Deref<Target = U>`. For example:
139148

@@ -163,20 +172,6 @@ r[coerce.types.deref]
163172
r[coerce.types.deref-mut]
164173
* `&mut T` to `&mut U` if `T` implements `DerefMut<Target = U>`.
165174

166-
r[coerce.types.unsize]
167-
* TyCtor(`T`) to TyCtor(`U`), where TyCtor(`T`) is one of
168-
- `&T`
169-
- `&mut T`
170-
- `*const T`
171-
- `*mut T`
172-
- `Box<T>`
173-
174-
and where `U` can be obtained from `T` by [unsized coercion](#unsized-coercions).
175-
176-
<!--In the future, coerce_inner will be recursively extended to tuples and
177-
structs. In addition, coercions from subtraits to supertraits will be
178-
added. See [RFC 401] for more details.-->
179-
180175
r[coerce.types.fn]
181176
* Function item types to `fn` pointers
182177

@@ -190,40 +185,90 @@ r[coerce.unsize]
190185
### Unsized Coercions
191186

192187
r[coerce.unsize.intro]
193-
The following coercions are called `unsized coercions`, since they
194-
relate to converting types to unsized types, and are permitted in a few
195-
cases where other coercions are not, as described above. They can still happen
196-
anywhere else a coercion can occur.
188+
The following coercions are called "unsized coercions", since their targets contain an unsized type.
189+
unsized coercions apply to pointer-like types which point to types which can lose some of their compile-time known information (such as size or implemented traits). For example:
190+
191+
```rust
192+
use std::cell::Cell;
193+
194+
fn main() {
195+
// `&[u8; 0]` can be coerced to `&[u8]`.
196+
//
197+
// here `&_` is the pointer-like type,
198+
// `[u8; 0]` is the original pointee,
199+
// and `[u8]` is more erased pointee (it lost the length information).
200+
let _: &[u8] = &[];
201+
202+
trait A: Super {}
203+
impl A for () {}
204+
205+
trait Super {}
206+
impl Super for () {}
207+
208+
// `&()` can be coerced to `&dyn A`, losing the type information.
209+
let a: &dyn A = &();
210+
211+
// `&dyn A` can be coerced to `&dyn Super`,
212+
// losing the fact that the underlying type (unit) implements `A` too.
213+
let _: &dyn Super = a;
197214

198-
r[coerce.unsize.trait]
199-
Two traits, [`Unsize`] and [`CoerceUnsized`], are used
200-
to assist in this process and expose it for library use. The following
201-
coercions are built-ins and, if `T` can be coerced to `U` with one of them, then
202-
an implementation of `Unsize<U>` for `T` will be provided:
215+
// The same coercions work with other pointer-like types and wrappers over them:
216+
let _: Box<[u8]> = Box::<[u8; 0]>::new([]);
217+
let _: Cell<Box<[u8]>> = Cell::new(Box::<[u8; 0]>::new([]));
218+
219+
// The result of the coercion doesn't *have* to be the same pointer-like type,
220+
// although this is only allowed for certain pairs of pointer-like types.
221+
let _: *const dyn A = &mut ();
222+
}
223+
```
224+
225+
r[coerce.unsize.confusion]
226+
> [!NOTE]
227+
> The term "unsized" might be quite confusing, since the coercion works on sized types (pointers) and the source pointer might point to an unsized type in the first place (`&dyn A -> &dyn Super` in the example above).
228+
>
229+
> "unsized" refers to the main purpose of these coercions --- converting (pointers to) sized types to (pointers to) unsized types. The pointers being not the focus, since unsized types can't exist without them.
230+
231+
r[coerce.unsize.metadata]
232+
When performing unsized coercion, the pointer metadata type changes. For example, when unsized `&u32` to `&dyn Debug` metadate type changes from `()` to `DynMetadata<dyn Debug>` (note that exact metadata types are not yet stable). This can also lead to a change in the pointer size -- `&u32` is half the size of `&dyn Debug`.
233+
234+
r[coerce.unsize.traits]
235+
Three internal traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process and expose it for library use.
236+
237+
r[coerce.unsize.traits.unsize]
238+
[`Unsize`] represents the fact that the target type is layout compatible with the source type and the pointer metadata of the target type can be derived from the metadata of the source, meaning that a pointer to the source type can be converted to a pointer to the target type. For example `[T; N]` implements `Unsize<[T]>` meaning that you can *unsize* former into the later, allowing coercions such as `&[T; N] -> &[T]`.
239+
240+
r[coerce.unsize.traits.coerce-unsized]
241+
[`CoerceUnsized`] represents the fact that a pointer-like type can be coerced to another pointer-like type, due to `Unsize` being implemented for their pointees. For example, `&T` implements `CoerceUnsized<&U>` when `T: Unsize<U>`.
242+
243+
r[coerce.unsize.traits.pin-coerce-unsized]
244+
[`PinCoerceUnsized`] is an unsafe marker trait for pointer-like types unsized coercion of which does not break [`Pin`] guarantees. It is a requirement of the `CoerceUnsized` implementation for `Pin`. That is, `&D: PinCoerceUnsized` implies `Pin<&T>: CoerceUnsized<Pin<&U>>`.
245+
246+
The following implementations of [`Unsize`] are built-in:
203247

204248
r[coerce.unsize.slice]
205-
* `[T; n]` to `[T]`.
249+
* `[T; n]: Unsize<[T]>`.
206250

207251
r[coerce.unsize.trait-object]
208-
* `T` to `dyn U`, when `T` implements `U + Sized`, and `U` is [dyn compatible].
252+
* `T: Unsize<dyn U>`, when `T` implements `U + Sized`, and `U` is [dyn compatible].
209253

210254
r[coerce.unsize.trait-upcast]
211-
* `dyn T` to `dyn U`, when `U` is one of `T`'s [supertraits].
212-
* This allows dropping auto traits, i.e. `dyn T + Auto` to `dyn U` is allowed.
213-
* This allows adding auto traits if the principal trait has the auto trait as a super trait, i.e. given `trait T: U + Send {}`, `dyn T` to `dyn T + Send` or to `dyn U + Send` coercions are allowed.
214-
215-
r[coerce.unsized.composite]
216-
* `Foo<..., T, ...>` to `Foo<..., U, ...>`, when:
217-
* `Foo` is a struct.
218-
* `T` implements `Unsize<U>`.
219-
* The last field of `Foo` has a type involving `T`.
220-
* If that field has type `Bar<T>`, then `Bar<T>` implements `Unsize<Bar<U>>`.
221-
* T is not part of the type of any other fields.
222-
223-
r[coerce.unsized.pointer]
224-
Additionally, a type `Foo<T>` can implement `CoerceUnsized<Foo<U>>` when `T`
225-
implements `Unsize<U>` or `CoerceUnsized<Foo<U>>`. This allows it to provide an
226-
unsized coercion to `Foo<U>`.
255+
* `dyn Trait: Unsize<dyn Super>`, when `Super` is one of `Trait`'s [supertraits].
256+
* This allows dropping auto traits, i.e. `dyn Trait + Auto` to `dyn Super` is allowed.
257+
* This allows adding auto traits if the principal trait has the auto trait as a super trait, i.e. given `trait Trait: Super + Auto {}`, `dyn Trait` to `dyn Trait + Auto` or to `dyn Super + Auto` coercions are allowed.
258+
259+
r[coerce.unsize.composite]
260+
* `S<T...>: Unsize<S<U...>>`, when:
261+
* `S` is a struct.
262+
* The type of the last field of `S<T...>` implements `Unsize<X>` where `X` is the type of the last field of `S<U...>`.
263+
* All generic arguments of `S` that are involved in its last field are only involved there (and not in other fields).
264+
* All generic arguments of `S` that are not involved in its last field are unchanged between `T...` and `U...`.
265+
266+
r[coerce.unsize.pointer]
267+
A type `S<T...>` *can* implement `CoerceUnsized<S<U...>>` if
268+
1. Only one field of `S<T...>` has different type than the same field of `S<U...>` (ignoring fields of type `PhantomData<_>`).
269+
2. ... and that field's type implements `CoerceUnsized<X>` where `X` is the type of the same field in `S<U,...>`.
270+
271+
This allows it to provide an unsized coercion to `S<U...>`.
227272

228273
> [!NOTE]
229274
> 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.
@@ -323,6 +368,8 @@ precisely.
323368
[subtype]: subtyping.md
324369
[dyn compatible]: items/traits.md#dyn-compatibility
325370
[type cast operator]: expressions/operator-expr.md#type-cast-expressions
371+
[`Pin`]: std::pin::Pin
372+
[`PinCoerceUnsized`]: std::pin::PinCoerceUnsized
326373
[`Unsize`]: std::marker::Unsize
327374
[`CoerceUnsized`]: std::ops::CoerceUnsized
328375
[method-call expressions]: expressions/method-call-expr.md

0 commit comments

Comments
 (0)