Skip to content

Commit

Permalink
Add some documentation for unsizing
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Nov 1, 2023
1 parent 7c28596 commit b0f1d49
Showing 1 changed file with 79 additions and 0 deletions.
79 changes: 79 additions & 0 deletions src/traits/unsize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# [`CoerceUnsized`](https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html)

`CoerceUnsized` is primarily concerned with data containers. When a struct
(typically, a smart pointer) implements `CoerceUnsized`, that means that the
data it points to is being unsized.

Some implementors of `CoerceUnsized` include:
* `&T`
* `Arc<T>`
* `Box<T>`

This trait is (eventually) intended to be implemented by user-written smart
pointers, and there are rules about when a type is allowed to implement
`CoerceUnsized` that are explained in the trait's documentation.

# [`Unsize`](https://doc.rust-lang.org/std/marker/trait.Unsize.html)

To contrast, the `Unsize` trait is concerned the actual types that are allowed
to be unsized.

This is not intended to be implemented by users ever, since `Unsize` does not
instruct the compiler (namely codegen) *how* to unsize a type, just whether it
is allowed to be unsized. This is paired somewhat intimately with codegen
which must understand how types are represented and unsized.

## Primitive unsizing implementations

Built-in implementations are provided for:
* `T` -> `dyn Trait + 'a` when `T: Trait` (and `T: Sized + 'a`, and `Trait`
is object safe).
* `[T; N]` -> `[T]`

## Structural implementations

There are two implementations of `Unsize` which can be thought of as
structural:
* `(A1, A2, .., An): Unsize<(A1, A2, .., U)> where An: Unsize<U>` allows the
tail field of a tuple to be unsized.
* `Struct<.., T, ..>: Unsize<Struct<.., U, ..>> where T: Unsize<U>` allows the
tail field of a struct to be unsized.

The rules for the latter implementation are slightly complicated, since they
may allow more than one parameter to be changed (not necessarily unsized) and
are best stated in terms of the tail field of the struct.

## Upcasting implementations

Two things are called "upcasting" internally:
1. True upcasting `dyn SubTrait` -> `dyn SuperTrait` (this also allows
dropping auto traits and adjusting lifetimes, as below).
2. Dropping auto traits and adjusting the lifetimes of dyn trait
*without changing the principal[^1]*:
`dyn Trait + AutoTraits... + 'a` -> `dyn Trait + NewAutoTraits... + 'b`
when `AutoTraits``NewAutoTraits`, and `'a: 'b`.

These may seem like different operations, since (1.) includes adjusting the
vtable of a dyn trait, while (2.) is a no-op. However, to the type system,
these are handled with much the same code.

This built-in implementation of `Unsize` is the most involved, particularly
after [it was reworked](https://github.com/rust-lang/rust/pull/114036) to
support the complexities of associated types.

Specifically, the upcasting algorithm involves: For each supertrait of the
source dyn trait's principal (including itself)...
1. Unify the super trait ref with the principal of the target (making sure
we only ever upcast to a true supertrait, and never [via an impl]).
2. For every auto trait in the source, check that it's present in the principal
(allowing us to drop auto traits, but never gain new ones).
3. For every projection in the source, check that it unifies with a single
projection in the target (since there may be more than one given
`trait Sub: Sup<.., A = i32> + Sup<.., A = u32>`).

[via an impl]: https://github.com/rust-lang/rust/blob/f3457dbf84cd86d284454d12705861398ece76c3/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs#L19

Specifically, (3.) prevents a choice of projection bound to guide inference
unnecessarily, though it may guide inference when it is unambiguous.

[^1]: The principal is the one non-auto trait of a `dyn Trait`.

0 comments on commit b0f1d49

Please sign in to comment.