diff --git a/src/glossary.md b/src/glossary.md index dabd65d7c..1a84692eb 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -188,6 +188,21 @@ A [*path*] is a sequence of one or more path segments used to refer to an [entity](#entity) in the current scope or other levels of a [namespace](#namespace) hierarchy. +r[glossary.pointer-metadata] +### Pointer metadata + +Pointer metadata is additional information associated with a raw pointer or reference type. + +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). + +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: + +* For structs whose last field is a DST, metadata is the metadata for the last field. +* For the `str` type, metadata is the length in bytes as `usize`. +* For slice types like `[T]`, metadata is the length in items as `usize`. +* For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata`][DynMetadata] + (e.g. `DynMetadata`). + ### Prelude 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 example of an uninhabited type is the [never type] `!`, or an enum with no variants `enum Never { }`. Opposite of [Inhabited](#inhabited). +[DynMetadata]: core::ptr::DynMetadata [alignment]: type-layout.md#size-and-alignment [associated item]: #associated-item [attributes]: attributes.md diff --git a/src/type-coercions.md b/src/type-coercions.md index a69b86968..2082594ae 100644 --- a/src/type-coercions.md +++ b/src/type-coercions.md @@ -134,6 +134,15 @@ r[coerce.types.ref-to-pointer] r[coerce.types.mut-to-pointer] * `&mut T` to `*mut T` +r[coerce.types.unsize] +* `T` to `U` if `T: CoerceUnsized`. For example: + ```rust + const _: &dyn std::fmt::Display = &0u8; // &u8 -> &dyn Display + const _: &[u32] = &[0, 1, 2, 3, 4, 5]; // &[u32; 4] -> &[u32] + ``` + + See [unsized coercion](#unsized-coercions) for more details. + r[coerce.types.deref] * `&T` or `&mut T` to `&U` if `T` implements `Deref`. For example: @@ -163,20 +172,6 @@ r[coerce.types.deref] r[coerce.types.deref-mut] * `&mut T` to `&mut U` if `T` implements `DerefMut`. -r[coerce.types.unsize] -* TyCtor(`T`) to TyCtor(`U`), where TyCtor(`T`) is one of - - `&T` - - `&mut T` - - `*const T` - - `*mut T` - - `Box` - - and where `U` can be obtained from `T` by [unsized coercion](#unsized-coercions). - - - r[coerce.types.fn] * Function item types to `fn` pointers @@ -190,40 +185,148 @@ r[coerce.unsize] ### Unsized Coercions r[coerce.unsize.intro] -The following coercions are called `unsized coercions`, since they -relate to converting types to unsized types, and are permitted in a few -cases where other coercions are not, as described above. They can still happen -anywhere else a coercion can occur. - -r[coerce.unsize.trait] -Two traits, [`Unsize`] and [`CoerceUnsized`], are used -to assist in this process and expose it for library use. The following -coercions are built-ins and, if `T` can be coerced to `U` with one of them, then -an implementation of `Unsize` for `T` will be provided: - -r[coerce.unsize.slice] -* `[T; n]` to `[T]`. - -r[coerce.unsize.trait-object] -* `T` to `dyn U`, when `T` implements `U + Sized`, and `U` is [dyn compatible]. - -r[coerce.unsize.trait-upcast] -* `dyn T` to `dyn U`, when `U` is one of `T`'s [supertraits]. - * This allows dropping auto traits, i.e. `dyn T + Auto` to `dyn U` is allowed. - * 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. - -r[coerce.unsized.composite] -* `Foo<..., T, ...>` to `Foo<..., U, ...>`, when: - * `Foo` is a struct. - * `T` implements `Unsize`. - * The last field of `Foo` has a type involving `T`. - * If that field has type `Bar`, then `Bar` implements `Unsize>`. - * T is not part of the type of any other fields. - -r[coerce.unsized.pointer] -Additionally, a type `Foo` can implement `CoerceUnsized>` when `T` -implements `Unsize` or `CoerceUnsized>`. This allows it to provide an -unsized coercion to `Foo`. +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: + +```rust +use std::cell::Cell; + +fn main() { + // `&[u8; 0]` can be coerced to `&[u8]`. + // + // Here `&_` is the pointer-like type, `[u8; 0]` is the original + // pointee, and `[u8]` is more erased pointee (losing the length). + let _: &[u8] = &[]; + + trait A: Super {} + impl A for () {} + + trait Super {} + impl Super for () {} + + // `&()` can be coerced to `&dyn A`, losing the type information. + let _: &dyn A = &(); + + // `&dyn A` can be coerced to `&dyn Super`, losing the fact that + // the underlying type (unit) implements `A` too. + let _: &dyn Super = &() as &dyn A; + + // The same coercions work with other pointer-like types and + // wrappers over them: + let _: Box<[u8]> = Box::<[u8; 0]>::new([]); + let _: Cell> = Cell::new(Box::<[u8; 0]>::new([])); + + // The result of the coercion doesn't *have* to be the same + // pointer-like type, although this is only allowed for certain + // pairs of pointer-like types. + let _: *const dyn A = &mut (); +} +``` + +> [!NOTE] +> 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). +> +> 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. + +> [!NOTE] +> 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` (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`. + +r[coerce.unsize.traits] +Three internal traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process. + +r[coerce.unsize.traits.unsize] +[`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. + +> [!EXAMPLE] +> Because `[T; N]` implements `Unsize<[T]>`, you can *unsize* `&[T; N]` into `&[T]`. + +r[coerce.unsize.traits.coerce-unsized] +[`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. + +> [!EXAMPLE] +> `&T` implements `CoerceUnsized<&U>` when `T: Unsize`. So, since `u8: Unsize`, `&u8: CoerceUnsized<&dyn Display>`. +> +> ```rust +> # #![ feature(coerce_unsized, unsize) ] +> # use core::{fmt::Display, marker::Unsize, ops::CoerceUnsized}; +> fn f() +> where +> // These bounds are "trivial". +> u8: Unsize, +> for<'a> &'a u8: CoerceUnsized<&'a (dyn Display + 'a)>, +> { +> let _: &dyn Display = &0u8; +> } +> ``` +> +> ```rust +> # #![ feature(coerce_unsized, unsize) ] +> # use core::{marker::Unsize, ops::CoerceUnsized}; +> fn f(x: T) +> where +> T: Unsize, +> for<'a> &'a T: CoerceUnsized<&'a U>, +> { +> let _: &U = &x; +> } +> ``` + +r[coerce.unsize.traits.pin-coerce-unsized] +[`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). + +> [!EXAMPLE] +> ```rust +> # #![ feature(coerce_unsized, pin_coerce_unsized_trait) ] +> # use core::{ops::CoerceUnsized, pin::{Pin, PinCoerceUnsized}}; +> trait Tr { fn f(); } +> impl Tr for (T, U) +> where +> // Assuming these are true... +> T: CoerceUnsized + PinCoerceUnsized, +> U: PinCoerceUnsized, +> { +> // ...we can prove this where clause: +> fn f() where Pin: CoerceUnsized> {} +> } +> ``` +> +> ```rust +> # #![ feature(coerce_unsized, pin_coerce_unsized_trait) ] +> # use core::{ops::CoerceUnsized, pin::{Pin, PinCoerceUnsized}}; +> fn f(x: Pin) +> where +> T: CoerceUnsized + PinCoerceUnsized, +> U: PinCoerceUnsized, +> { +> let _: Pin = x; +> } +> ``` + +r[coerce.unsize.built-in] +The following implementations of [`Unsize`] are built-in: + +r[coerce.unsize.built-in.slice] +* `[T; n]: Unsize<[T]>`. + +r[coerce.unsize.built-in.trait-object] +* `T: Unsize`, when `T` implements `U + Sized`, and `U` is [dyn compatible]. + +r[coerce.unsize.built-in.trait-upcast] +* `dyn Trait: Unsize`, when `Super` is one of `Trait`'s [supertraits]. + * This allows dropping auto traits, i.e. `dyn Trait + Auto` to `dyn Super` is allowed. + * 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. + +r[coerce.unsize.built-in.composite] +* `S: Unsize>`, when: + * `S<..>` is a struct. + * Where `F<..>` is the type of the last field of `S<..>`, `F: Unsize>`. + +r[coerce.unsize.pointer] +A type `S` *can* implement `CoerceUnsized>` if both: + +* Only one field of `S` has a different type than the same field of `S` (ignoring fields of type `PhantomData<_>`). +* That field's type implements `CoerceUnsized` where `X` is the type of the corresponding field in `S`. + +This allows `S` types to be coerced to `S`. > [!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. @@ -318,11 +421,15 @@ This description is obviously informal. Making it more precise is expected to proceed as part of a general effort to specify the Rust type checker more precisely. +[#81513]: https://github.com/rust-lang/rust/issues/81513 [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md [RFC 1558]: https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md [subtype]: subtyping.md [dyn compatible]: items/traits.md#dyn-compatibility +[pointer metadata]: glossary.pointer-metadata [type cast operator]: expressions/operator-expr.md#type-cast-expressions +[`Pin`]: std::pin::Pin +[`PinCoerceUnsized`]: std::pin::PinCoerceUnsized [`Unsize`]: std::marker::Unsize [`CoerceUnsized`]: std::ops::CoerceUnsized [method-call expressions]: expressions/method-call-expr.md