Trait KnownLayout
unsafe trait KnownLayout
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.:
# use 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:
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 presence 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:
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 thatx == size_of::<T>() - If
Tis a slice DST, then we mean that there exists alensuch that the instance ofTwithlentrailing slice elements has sizex
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 meansize_of::<T>()if the byte slice is at leastsize_of::<T>()bytes long - If
Tis a slice DST, then we mean to consider all values,len, such that the instance ofTwithlentrailing slice elements fits in the byte slice, and to choose the largest suchlen, 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.
Associated Types
type PointerMetadata: TraitBound { trait_: Path { path: "PointerMetadata", id: Id(3645), args: None }, generic_params: [], modifier: None }The type of metadata stored in a pointer to
Self.This is
()for sized types andusizefor slice DSTs.
Provided Methods
fn size_for_metadata(meta: <Self as >::PointerMetadata) -> Option<usize>Computes the size of an object of type
Selfwith the given pointer metadata.Safety
size_for_metadatapromises to returnNoneif and only if the resulting size would not fit in ausize. Note that the returned size could exceed the actual maximum valid size of an allocated object,isize::MAX.Examples
use KnownLayout; assert_eq!; assert_eq!; assert_eq!; assert_eq!; // This size exceeds the maximum valid object size (`isize::MAX`): assert_eq!; // This size, if computed, would exceed `usize::MAX`: assert_eq!;
Implementors
impl<O> KnownLayout for F32<O>impl<O> KnownLayout for I64<O>impl KnownLayout for NonZeroI128impl KnownLayout for u128impl KnownLayout for AtomicU8impl KnownLayout for __m256dimpl KnownLayout for f64impl KnownLayout for AtomicU32impl KnownLayout for __m512iimpl<T: ?Sized> KnownLayout for &mut Timpl KnownLayout for NonZeroI16impl KnownLayout for ()impl<O> KnownLayout for U16<O>impl KnownLayout for AtomicIsizeimpl KnownLayout for u32impl KnownLayout for NonZeroU128impl<O> KnownLayout for I16<O>impl KnownLayout for i128impl KnownLayout for __m256impl<O> KnownLayout for I32<O>impl KnownLayout for boolimpl KnownLayout for AtomicI16impl KnownLayout for __m512dimpl KnownLayout for NonZeroU16impl<T, N: usize> KnownLayout for [T; N]impl KnownLayout for u8impl<O> KnownLayout for Isize<O>impl KnownLayout for AtomicUsizeimpl KnownLayout for NonZeroI64impl<O> KnownLayout for Usize<O>impl KnownLayout for i32impl KnownLayout for __m128iimpl KnownLayout for usizeimpl<T> KnownLayout for Option<T>impl<O> KnownLayout for U64<O>impl KnownLayout for AtomicU16impl KnownLayout for charimpl KnownLayout for __m512impl<T: ?Sized> KnownLayout for *const Timpl KnownLayout for NonZeroI8impl KnownLayout for strimpl KnownLayout for AtomicI64impl KnownLayout for i8impl<T: ?Sized + KnownLayout> KnownLayout for UnsafeCell<T>impl KnownLayout for NonZeroU64impl KnownLayout for u64impl KnownLayout for __m128dimpl KnownLayout for NonZeroIsizeimpl KnownLayout for isizeimpl<T: ?Sized> KnownLayout for PhantomData<T>impl KnownLayout for AtomicBoolimpl<O> KnownLayout for F64<O>impl KnownLayout for __m512bhimpl<T> KnownLayout for CoreMaybeUninit<T>impl KnownLayout for NonZeroU8impl<O> KnownLayout for I128<O>impl<T: ?Sized> KnownLayout for *mut Timpl KnownLayout for AtomicU64impl<T: ?Sized + KnownLayout> KnownLayout for ManuallyDrop<T>impl KnownLayout for NonZeroI32impl KnownLayout for u16impl<T: ?Sized + KnownLayout> KnownLayout for Cell<T>impl KnownLayout for __m128impl KnownLayout for i64impl KnownLayout for NonZeroUsizeimpl KnownLayout for AtomicI8impl KnownLayout for f32impl KnownLayout for __m256iimpl<T> KnownLayout for Wrapping<T>impl<T> KnownLayout for Unalign<T>impl<T: ?Sized> KnownLayout for &Timpl KnownLayout for AtomicI32impl<O> KnownLayout for U32<O>impl<T> KnownLayout for [T]impl KnownLayout for NonZeroU32impl<O> KnownLayout for U128<O>impl KnownLayout for i16impl<T> KnownLayout for AtomicPtr<T>