Skip to content

Commit e2262c8

Browse files
committed
WIP: Revise editorially
Let's make some editorial revisions to clarify wording and add and clarify examples. One thing we're struggling with a bit here is how much we want to overlap with the documentation of the standard library. In some cases, we've removed detailed descriptions of the implementation rule when this is better covered in the library docs. Thanks to Eric Huss for the glossary entry added here.
1 parent a2c9f86 commit e2262c8

File tree

2 files changed

+105
-29
lines changed

2 files changed

+105
-29
lines changed

src/glossary.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,21 @@ A [*path*] is a sequence of one or more path segments used to refer to an
188188
[entity](#entity) in the current scope or other levels of a
189189
[namespace](#namespace) hierarchy.
190190

191+
r[glossary.pointer-metadata]
192+
### Pointer metadata
193+
194+
Pointer metadata is additional information associated with a raw pointer or reference type.
195+
196+
Pointers for statically sized types (that implement the [`Sized`] trait) and `extern` types are said to be "thin" and their metadata is of type `()` (and is zero-sized).
197+
198+
Pointers for [dynamically sized types] (DSTs) are said to be "wide" or "fat" and consist of the data pointer (that contains the memory address of the value) and some non-zero-sized metadata:
199+
200+
* For structs whose last field is a DST, metadata is the metadata for the last field.
201+
* For the `str` type, metadata is the length in bytes as `usize`.
202+
* For slice types like `[T]`, metadata is the length in items as `usize`.
203+
* For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata<Self>`][DynMetadata]
204+
(e.g. `DynMetadata<dyn SomeTrait>`).
205+
191206
### Prelude
192207

193208
Prelude, or The Rust Prelude, is a small collection of items - mostly traits - that are
@@ -290,6 +305,7 @@ uninhabited type is "empty" in the sense that there are no values of the type. T
290305
example of an uninhabited type is the [never type] `!`, or an enum with no variants
291306
`enum Never { }`. Opposite of [Inhabited](#inhabited).
292307

308+
[DynMetadata]: core::ptr::DynMetadata
293309
[alignment]: type-layout.md#size-and-alignment
294310
[associated item]: #associated-item
295311
[attributes]: attributes.md

src/type-coercions.md

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,16 @@ r[coerce.unsize]
185185
### Unsized Coercions
186186

187187
r[coerce.unsize.intro]
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:
188+
The following coercions are called "unsized coercions", since their targets contain an unsized type. Unsized coercions apply to pointer-like types where some type information known about the referent at compile-time (e.g. its size or traits that it implements) can be *erased*. For example:
190189

191190
```rust
192191
use std::cell::Cell;
193192

194193
fn main() {
195194
// `&[u8; 0]` can be coerced to `&[u8]`.
196195
//
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).
196+
// Here `&_` is the pointer-like type, `[u8; 0]` is the original
197+
// pointee, and `[u8]` is more erased pointee (losing the length).
200198
let _: &[u8] = &[];
201199

202200
trait A: Super {}
@@ -206,41 +204,102 @@ fn main() {
206204
impl Super for () {}
207205

208206
// `&()` can be coerced to `&dyn A`, losing the type information.
209-
let a: &dyn A = &();
207+
let _: &dyn A = &();
210208

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;
209+
// `&dyn A` can be coerced to `&dyn Super`, losing the fact that
210+
// the underlying type (unit) implements `A` too.
211+
let _: &dyn Super = &() as &dyn A;
214212

215-
// The same coercions work with other pointer-like types and wrappers over them:
213+
// The same coercions work with other pointer-like types and
214+
// wrappers over them:
216215
let _: Box<[u8]> = Box::<[u8; 0]>::new([]);
217216
let _: Cell<Box<[u8]>> = Cell::new(Box::<[u8; 0]>::new([]));
218217

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.
218+
// The result of the coercion doesn't *have* to be the same
219+
// pointer-like type, although this is only allowed for certain
220+
// pairs of pointer-like types.
221221
let _: *const dyn A = &mut ();
222222
}
223223
```
224224

225225
> [!NOTE]
226-
> 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).
226+
> The term "unsized" might be confusing, as the coercion works on sized types (the pointer-like type itself) and the source pointer might point to an unsized type in the first place (e.g. `&dyn A -> &dyn Super` in the example above).
227227
>
228-
> "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.
228+
> Here, "unsized" refers to the main purpose of these coercions, which is to produce (pointers to) unsized types. Since unsized types can't exist except behind a pointer, the pointers are deemphasized.
229229
230-
r[coerce.unsize.metadata]
231-
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`.
230+
> [!NOTE]
231+
> When doing an unsized coercion, the internal [pointer metadata] type changes. For example, when coercing `&u32` to `&dyn Debug`, the metadata type changes from `()` to `DynMetadata<dyn Debug>` (these metadata types are not yet stable, see [#81513]). This can also lead to a change in the pointer size --- `&u32` is half the size of `&dyn Debug`.
232232
233233
r[coerce.unsize.traits]
234-
Three internal traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process and expose it for library use.
234+
Three internal traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process.
235235

236236
r[coerce.unsize.traits.unsize]
237-
[`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]`.
237+
[`Unsize`] represents 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. This implies that a pointer to the source type can be converted to a pointer to the target type.
238+
239+
> [!EXAMPLE]
240+
> Because `[T; N]` implements `Unsize<[T]>`, you can *unsize* `&[T; N]` into `&[T]`.
238241
239242
r[coerce.unsize.traits.coerce-unsized]
240-
[`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>`.
243+
[`CoerceUnsized`] represents that a pointer-like type can be coerced to another pointer-like type when `Unsize` is implemented for the pointee of the source type.
244+
245+
> [!EXAMPLE]
246+
> `&T` implements `CoerceUnsized<&U>` when `T: Unsize<U>`. So, since `u8: Unsize<dyn Display>`, `&u8: CoerceUnsized<&dyn Display>`.
247+
>
248+
> ```rust
249+
> # #![ feature(coerce_unsized, unsize) ]
250+
> # use core::{fmt::Display, marker::Unsize, ops::CoerceUnsized};
251+
> fn f()
252+
> where
253+
> // These bounds are "trivial".
254+
> u8: Unsize<dyn Display>,
255+
> for<'a> &'a u8: CoerceUnsized<&'a (dyn Display + 'a)>,
256+
> {
257+
> let _: &dyn Display = &0u8;
258+
> }
259+
> ```
260+
>
261+
> ```rust
262+
> # #![ feature(coerce_unsized, unsize) ]
263+
> # use core::{marker::Unsize, ops::CoerceUnsized};
264+
> fn f<T, U>(x: T)
265+
> where
266+
> T: Unsize<U>,
267+
> for<'a> &'a T: CoerceUnsized<&'a U>,
268+
> {
269+
> let _: &U = &x;
270+
> }
271+
> ```
241272
242273
r[coerce.unsize.traits.pin-coerce-unsized]
243-
[`PinCoerceUnsized`] is an unsafe marker trait for pointer-like types, unsized coercion of which does not break [`Pin`] guarantees. [`PinCoerceUnsized`] being implemented for the pointer is a requirement of the `CoerceUnsized` implementation for `Pin`. That is, `&D: PinCoerceUnsized` implies `Pin<&T>: CoerceUnsized<Pin<&U>>`.
274+
[`PinCoerceUnsized`] is an unsafe marker trait that is implemented for pointer-like types to indicate it is safe to perform an unsized coercion when the pointee is pinned (and must therefore uphold the [`Pin`] guarantees).
275+
276+
> [!EXAMPLE]
277+
> ```rust
278+
> # #![ feature(coerce_unsized, pin_coerce_unsized_trait) ]
279+
> # use core::{ops::CoerceUnsized, pin::{Pin, PinCoerceUnsized}};
280+
> trait Tr { fn f(); }
281+
> impl<T, U> Tr for (T, U)
282+
> where
283+
> // Assuming these are true...
284+
> T: CoerceUnsized<U> + PinCoerceUnsized,
285+
> U: PinCoerceUnsized,
286+
> {
287+
> // ...we can prove this where clause:
288+
> fn f() where Pin<T>: CoerceUnsized<Pin<U>> {}
289+
> }
290+
> ```
291+
>
292+
> ```rust
293+
> # #![ feature(coerce_unsized, pin_coerce_unsized_trait) ]
294+
> # use core::{ops::CoerceUnsized, pin::{Pin, PinCoerceUnsized}};
295+
> fn f<T, U>(x: Pin<T>)
296+
> where
297+
> T: CoerceUnsized<U> + PinCoerceUnsized,
298+
> U: PinCoerceUnsized,
299+
> {
300+
> let _: Pin<U> = x;
301+
> }
302+
> ```
244303
245304
r[coerce.unsize.built-in]
246305
The following implementations of [`Unsize`] are built-in:
@@ -257,18 +316,17 @@ r[coerce.unsize.built-in.trait-upcast]
257316
* 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.
258317
259318
r[coerce.unsize.built-in.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...`.
319+
* `S<A0, .., An, T0, .., Tn, B0, .., Bn>: Unsize<S<A0, .., An, .., U0, .., Un, .., B0, .., Bn>>`, when:
320+
* `S<..>` is a struct.
321+
* Where `F<..>` is the type of the last field of `S<..>`, `F<T0, .., Tn>: Unsize<F<U0, .., Un>>`.
265322
266323
r[coerce.unsize.pointer]
267-
A type `S<T...>` *can* implement `CoerceUnsized<S<U...>>` if
268-
1. Only one field of `S<T...>` has a 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,...>`.
324+
A type `S<T0, .., Tn>` *can* implement `CoerceUnsized<S<U0, .., Un>>` if both:
325+
326+
* Only one field of `S<T0, .., Tn>` has a different type than the same field of `S<U0, .., Un>` (ignoring fields of type `PhantomData<_>`).
327+
* That field's type implements `CoerceUnsized<X>` where `X` is the type of the corresponding field in `S<U0, .., Un>`.
270328
271-
This allows it to provide an unsized coercion to `S<U...>`.
329+
This allows `S<T0, .., Tn>` types to be coerced to `S<U0, .., Un>`.
272330
273331
> [!NOTE]
274332
> 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.
@@ -363,10 +421,12 @@ This description is obviously informal. Making it more precise is expected to
363421
proceed as part of a general effort to specify the Rust type checker more
364422
precisely.
365423

424+
[#81513]: https://github.com/rust-lang/rust/issues/81513
366425
[RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
367426
[RFC 1558]: https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md
368427
[subtype]: subtyping.md
369428
[dyn compatible]: items/traits.md#dyn-compatibility
429+
[pointer metadata]: glossary.pointer-metadata
370430
[type cast operator]: expressions/operator-expr.md#type-cast-expressions
371431
[`Pin`]: std::pin::Pin
372432
[`PinCoerceUnsized`]: std::pin::PinCoerceUnsized

0 commit comments

Comments
 (0)