[][src]Module typic::transmute::unsafe_transmutation

Guidance and tools for sound transmutation.

A transmutation is sound if the mere act of transmutation is guaranteed to not violate Rust's memory model.

When is a transmutation sound?

A transmutation is only sound if it occurs between types with well-defined representations, and does not violate Rust's memory model. See Transmutations Between Owned Values, and Transmutations Between References. These rules are automatically enforced by unsafe_transmute and TransmuteInto<U>.

Well-Defined Representation

Transmutation is always unsound if it occurs between types with unspecified representations. Most of Rust's primitive types have specified representations. That is, the layout characteristics of u8, f32 and others are guaranteed to be stable across compiler versions.

In contrast, most struct and enum types defined without an explicit #[repr(C)] or #[repr(transparent)] attribute do not have well-specified layout characteristics.

To ensure that types you've define are soundly transmutable, you usually must mark them with the #[repr(C)] attribute.

Transmuting Owned Values

Transmutations involving owned values must adhere to two rules to be sound. They must:

Preserve or Broaden Bit Validity

For each ith of the destination type, all possible instantiations of the ith byte of the source type must be a bit-valid instance of the ith byte of the destination type.

For example, we are permitted us to transmute a NonZeroU8 into a u8:

let _ : u8 = NonZeroU8::new(1).unwrap().transmute_into();

...because all possible instances of NonZeroU8 are also valid instances of u8. However, transmuting a u8 into a NonZeroU8 is forbidden:

This example deliberately fails to compile
let _ : NonZeroU8 = u8::default().transmute_into(); // Compile Error!

...because not all instances of u8 are valid instances of NonZeroU8.

Another example: While laying out certain types, rust may insert padding bytes between the layouts of fields. In the below example Padded has two padding bytes, while Packed has none:

#[typic::repr(C)]
#[derive(Default, StableABI)]
struct Padded(pub u8, pub u16, pub u8);

#[typic::repr(C)]
#[derive(Default, StableABI)]
struct Packed(pub u16, pub u16, pub u16);

assert_eq!(mem::size_of::<Packed>(), mem::size_of::<Padded>());

We may safely transmute from Packed to Padded:

let _ : Padded = Packed::default().transmute_into();

...but not from Padded to Packed:

This example deliberately fails to compile
let _ : Packed = Padded::default().transmute_into(); // Compile Error!

...because doing so would expose two uninitialized padding bytes in Padded as if they were initialized bytes in Packed.

Preserve or Shrink Size

It's completely safe to transmute into a type with fewer bytes than the destination type; e.g.:

let _ : u8 = u64::default().transmute_into();

This transmute truncates away the final three bytes of the u64 value.

A value may not be transmuted into a type of greater size:

This example deliberately fails to compile
let _ : u64 = u8::default().transmute_into(); // Compile Error!

Transmuting References

The restrictions above that to transmuting owned values, also apply to transmuting references. However, references carry a few additional restrictions. A sound transmutation must:

Preserve or Relax Alignment

You may transmute a reference into reference of more relaxed alignment:

let _: &[u8; 0] = (&[0u16; 0]).transmute_into();

However, you may not transmute a reference into a reference of stricter alignment:

This example deliberately fails to compile
let _: &[u16; 0] = (&[0u8; 0]).transmute_into(); // Compile Error!

Preserve or Shrink Lifetimes

You may transmute a reference into reference of lesser lifetime:

fn shrink<'a>() -> &'a u8 {
    static long : &'static u8 =  &16;
    long
}

However, you may not transmute a reference into a reference of greater lifetime:

This example deliberately fails to compile
fn extend<'a>(short: &'a u8) -> &'static u8 {
    static long : &'static u8 =  &16;
    short.transmute_into()
}

Preserve or Shrink Mutability

You may preserve or decrease the mutability of a reference through transmutation:

let _: &u8 = (&42u8).transmute_into();
let _: &u8 = (&mut 42u8).transmute_into();

However, you may not transmute an immutable reference into a mutable reference:

This example deliberately fails to compile
let _: &mut u8 = (&42u8).transmute_into(); // Compile Error!

Preserve Validity

Unlike transmutations of owned values, the transmutation of a reference may also not expand the bit-validity of the referenced type. For instance:

This example deliberately fails to compile
let mut x = NonZeroU8::new(42).unwrap();
{
    let y : &mut u8 = (&mut x).transmute_into(); // Compile Error!
    *y = 0;
}

let z : NonZeroU8 = x;

If this example did not produce a compile error, the value of z would not be a bit-valid instance of its type.

Modules

neglect

Configuration options for sound transmutations.

Traits

UnsafeTransmuteFrom

A sound value-to-value conversion. The reciprocal of UnsafeTransmuteInto.

UnsafeTransmuteInto

A sound value-to-value conversion. The reciprocal of UnsafeTransmuteFrom.

UnsafeTransmuteOptions

Options for unsafe transmutation.

Functions

unsafe_transmute

A sound value-to-value conversion.