zerocopy

Trait KnownLayout

Source
pub unsafe trait KnownLayout {
    type PointerMetadata: PointerMetadata;
}
Expand description

Indicates that zerocopy can reason about certain aspects of a type’s layout.

This trait is required by many of zerocopy’s APIs. It supports sized types, slices, and slice DSTs.

§Implementation

Do not implement this trait yourself! Instead, use #[derive(KnownLayout)]; e.g.:

#[derive(KnownLayout)]
struct MyStruct {
    ...
}

#[derive(KnownLayout)]
enum MyEnum {
    ...
}

#[derive(KnownLayout)]
union MyUnion {
    ...
}

This derive performs a sophisticated analysis to deduce the layout characteristics of types. You must implement this trait via the derive.

§Dynamically-sized types

KnownLayout supports slice-based dynamically sized types (“slice DSTs”).

A slice DST is a type whose trailing field is either a slice or another slice DST, rather than a type with fixed size. For example:

#[repr(C)]
struct PacketHeader {
    ...
}

#[repr(C)]
struct Packet {
    header: PacketHeader,
    body: [u8],
}

It can be useful to think of slice DSTs as a generalization of slices - in other words, a normal slice is just the special case of a slice DST with zero leading fields. In particular:

  • Like slices, slice DSTs can have different lengths at runtime
  • Like slices, slice DSTs cannot be passed by-value, but only by reference or via other indirection such as Box
  • Like slices, a reference (or Box, or other pointer type) to a slice DST encodes the number of elements in the trailing slice field

§Slice DST layout

Just like other composite Rust types, the layout of a slice DST is not well-defined unless it is specified using an explicit #[repr(...)] attribute such as #[repr(C)]. Other representations are supported, but in this section, we’ll use #[repr(C)] as our example.

A #[repr(C)] slice DST is laid out just like sized #[repr(C)] types, but the presenence of a variable-length field introduces the possibility of dynamic padding. In particular, it may be necessary to add trailing padding after the trailing slice field in order to satisfy the outer type’s alignment, and the amount of padding required may be a function of the length of the trailing slice field. This is just a natural consequence of the normal #[repr(C)] rules applied to slice DSTs, but it can result in surprising behavior. For example, consider the following type:

#[repr(C)]
struct Foo {
    a: u32,
    b: u8,
    z: [u16],
}

Assuming that u32 has alignment 4 (this is not true on all platforms), then Foo has alignment 4 as well. Here is the smallest possible value for Foo:

byte offset | 01234567
      field | aaaab---
                   ><

In this value, z has length 0. Abiding by #[repr(C)], the lowest offset that we can place z at is 5, but since z has alignment 2, we need to round up to offset 6. This means that there is one byte of padding between b and z, then 0 bytes of z itself (denoted >< in this diagram), and then two bytes of padding after z in order to satisfy the overall alignment of Foo. The size of this instance is 8 bytes.

What about if z has length 1?

byte offset | 01234567
      field | aaaab-zz

In this instance, z has length 1, and thus takes up 2 bytes. That means that we no longer need padding after z in order to satisfy Foo’s alignment. We’ve now seen two different values of Foo with two different lengths of z, but they both have the same size - 8 bytes.

What about if z has length 2?

byte offset | 012345678901
      field | aaaab-zzzz--

Now z has length 2, and thus takes up 4 bytes. This brings our un-padded size to 10, and so we now need another 2 bytes of padding after z to satisfy Foo’s alignment.

Again, all of this is just a logical consequence of the #[repr(C)] rules applied to slice DSTs, but it can be surprising that the amount of trailing padding becomes a function of the trailing slice field’s length, and thus can only be computed at runtime.

§What is a valid size?

There are two places in zerocopy’s API that we refer to “a valid size” of a type. In normal casts or conversions, where the source is a byte slice, we need to know whether the source byte slice is a valid size of the destination type. In prefix or suffix casts, we need to know whether there exists a valid size of the destination type which fits in the source byte slice and, if so, what the largest such size is.

As outlined above, a slice DST’s size is defined by the number of elements in its trailing slice field. However, there is not necessarily a 1-to-1 mapping between trailing slice field length and overall size. As we saw in the previous section with the type Foo, instances with both 0 and 1 elements in the trailing z field result in a Foo whose size is 8 bytes.

When we say “x is a valid size of T”, we mean one of two things:

  • If T: Sized, then we mean that x == size_of::<T>()
  • If T is a slice DST, then we mean that there exists a len such that the instance of T with len trailing slice elements has size x

When we say “largest possible size of T that fits in a byte slice”, we mean one of two things:

  • If T: Sized, then we mean size_of::<T>() if the byte slice is at least size_of::<T>() bytes long
  • If T is a slice DST, then we mean to consider all values, len, such that the instance of T with len trailing slice elements fits in the byte slice, and to choose the largest such len, if any

§Safety

This trait does not convey any safety guarantees to code outside this crate.

You must not rely on the #[doc(hidden)] internals of KnownLayout. Future releases of zerocopy may make backwards-breaking changes to these items, including changes that only affect soundness, which may cause code which uses those items to silently become unsound.

Required Associated Types§

Source

type PointerMetadata: PointerMetadata

The type of metadata stored in a pointer to Self.

This is () for sized types and usize for slice DSTs.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl KnownLayout for ()

Source§

impl KnownLayout for AtomicBool

Source§

impl KnownLayout for AtomicI8

Source§

impl KnownLayout for AtomicI16

Source§

impl KnownLayout for AtomicI32

Source§

impl KnownLayout for AtomicI64

Source§

impl KnownLayout for AtomicIsize

Source§

impl KnownLayout for AtomicU8

Source§

impl KnownLayout for AtomicU16

Source§

impl KnownLayout for AtomicU32

Source§

impl KnownLayout for AtomicU64

Source§

impl KnownLayout for AtomicUsize

Source§

impl KnownLayout for NonZeroI8

Source§

impl KnownLayout for NonZeroI16

Source§

impl KnownLayout for NonZeroI32

Source§

impl KnownLayout for NonZeroI64

Source§

impl KnownLayout for NonZeroI128

Source§

impl KnownLayout for NonZeroIsize

Source§

impl KnownLayout for NonZeroU8

Source§

impl KnownLayout for NonZeroU16

Source§

impl KnownLayout for NonZeroU32

Source§

impl KnownLayout for NonZeroU64

Source§

impl KnownLayout for NonZeroU128

Source§

impl KnownLayout for NonZeroUsize

Source§

impl KnownLayout for bool

Source§

impl KnownLayout for char

Source§

impl KnownLayout for f32

Source§

impl KnownLayout for f64

Source§

impl KnownLayout for i8

Source§

impl KnownLayout for i16

Source§

impl KnownLayout for i32

Source§

impl KnownLayout for i64

Source§

impl KnownLayout for i128

Source§

impl KnownLayout for isize

Source§

impl KnownLayout for str

Source§

impl KnownLayout for u8

Source§

impl KnownLayout for u16

Source§

impl KnownLayout for u32

Source§

impl KnownLayout for u64

Source§

impl KnownLayout for u128

Source§

impl KnownLayout for usize

Source§

impl<T> KnownLayout for AtomicPtr<T>

Source§

impl<T> KnownLayout for MaybeUninit<T>

Source§

impl<T> KnownLayout for Option<T>

Source§

impl<T> KnownLayout for Wrapping<T>

Source§

impl<T> KnownLayout for [T]

Source§

impl<T, const N: usize> KnownLayout for [T; N]

Source§

impl<T: ?Sized + KnownLayout> KnownLayout for ManuallyDrop<T>

Source§

impl<T: ?Sized + KnownLayout> KnownLayout for UnsafeCell<T>

Source§

impl<T: ?Sized> KnownLayout for *const T

Source§

impl<T: ?Sized> KnownLayout for *mut T

Source§

impl<T: ?Sized> KnownLayout for PhantomData<T>

Implementors§

Source§

impl<O> KnownLayout for F32<O>
where Self: Sized,

Source§

impl<O> KnownLayout for F64<O>
where Self: Sized,

Source§

impl<O> KnownLayout for I16<O>
where Self: Sized,

Source§

impl<O> KnownLayout for I32<O>
where Self: Sized,

Source§

impl<O> KnownLayout for I64<O>
where Self: Sized,

Source§

impl<O> KnownLayout for I128<O>
where Self: Sized,

Source§

impl<O> KnownLayout for Isize<O>
where Self: Sized,

Source§

impl<O> KnownLayout for U16<O>
where Self: Sized,

Source§

impl<O> KnownLayout for U32<O>
where Self: Sized,

Source§

impl<O> KnownLayout for U64<O>
where Self: Sized,

Source§

impl<O> KnownLayout for U128<O>
where Self: Sized,

Source§

impl<O> KnownLayout for Usize<O>
where Self: Sized,

Source§

impl<T> KnownLayout for Unalign<T>