D   A   T   A   W   O   K





Creation: December 06 2021
Modified: February 05 2022

Rust Coercions

Types can implicitly be coerced to change in certain contexts.

These changes are generally just weakening of types, largely focused around pointers and lifetimes.

They mostly exist to make Rust "just work" in more cases, and are largely harmless.


There are four possible coercions categories in Rust:

1. Transitivity.

If T coerces to U and U coerces to V, then T coerces to V.

2. Type weakening

3. Deref trait

If T: Deref<Target = U>, then &T coerces to &U via the deref() method. (Similarly, if T: DerefMut, then &mut T coerces to &mut U via deref_mut())

4. Unsize trait:

Types that can be "unsized" to a dynamically-sized type.

If Ptr is a "pointer type" (e.g. &T, *mut T, Box, Rc, etc), and T: Unsize<U>, then Ptr<T> coerces to Ptr<U>.

The Unsize trait is automatically implemented for:

Unsize is used along with ops::CoerceUnsized to allow "user-defined" containers such as Rc to contain dynamically-sized types.

Rust recognizes Ptr<X> as a "pointer type" if it implements CoerceUnsized. The actual rule is stated as, "if T: CoerceUnsized<U> then T coerces to U".)

All implementations of Unsize are provided automatically by the compiler.


Array to Slice coercions

The reason &[T; n] coerces to &[T] is rule 4:

Using these, &[T; n] can coerce to &[T].


Trait bounds (no) coercion

Note that coercion is not performed when matching traits.

If there is an implementation for some type U and T coerces to U, that does not constitute an implementation for T. For example, the following will not type check, even though it is ok to coerce it to &T and there is an implementation for &T:

trait Trait {}

fn foo<X: Trait>(t: X) {}

impl<'a> Trait for &'a i32 {}

fn main() {
    let t: &mut i32 = &mut 0;
    foo(t);
}

Which fails like as follows:

error[E0277]: the trait bound `&mut i32: Trait` is not satisfied
 --> src/main.rs:9:9
  |
3 | fn foo<X: Trait>(t: X) {}
  |           ----- required by this bound in `foo`
...
9 |     foo(t);
  |         ^ the trait `Trait` is not implemented for `&mut i32`
  |
  = help: the following implementations were found:
            <&'a i32 as Trait>
  = note: `Trait` is implemented for `&i32`, but not for `&mut i32`

Proudly self-hosted on a cheap Raspberry Pi