Memory Layout and Representation

Memory Layout Guarantees

Memory layout guarantees are the documented contracts in the Rust language specification that define when unsafe code, FFI bindings, and serialization routines can rely on a type's size, alignment, field offsets, and valid bit patterns. Relying on layout properties outside these guarantees is undefined behavior if a future compiler version or optimization pass changes the layout.

Complete Reference of Layout Guarantees

Type or Construct Guaranteed Size Guaranteed Alignment Additional Guarantees
bool 1 byte 1 byte Bit pattern 0x00 is false, 0x01 is true
u8 / i8, u16 / i16, u32 / i32, u64 / i64, u128 / i128 1, 2, 4, 8, 16 bytes Platform ABI (up to size) Two's complement for signed integers; all bit patterns valid
f32, f64 4, 8 bytes Platform ABI IEEE 754 binary32 and binary64 formats
char 4 bytes Platform ABI (4 on major targets) Valid Unicode scalar value (0x0..=0xD7FF, 0xE000..=0x10FFFF)
usize, isize Same as *const () Same as *const () Large enough to address any byte in memory
&T, &mut T, Box<T>, NonNull<T> (where T: Sized) Same as *const T (1 word) Same as *const T Non-null and aligned; Option<Ptr> has the same layout as nullable *const T
&[T], &mut [T], Box<[T]> 2 * size_of::<usize>() align_of::<usize>() Contiguous len elements of T in memory (note: order of (ptr, len) inside the fat pointer itself is not guaranteed)
[T; N] N * size_of::<T>() align_of::<T>() Contiguous elements at offsets i * size_of::<T>() with zero inter-element padding
(), unit structs, [T; 0], PhantomData<T> 0 bytes $\ge 1$ byte Occupies no memory
#[repr(transparent)] struct W(T) size_of::<T>() align_of::<T>() Identical layout, niches, and function-call ABI as T
#[repr(C)] struct (with non-ZST fields) C ABI struct size $\max(\text{align}(\text{fields}))$ Sequential field offsets matching target C struct layout
Option<T> where T is &U, &mut U, Box<U>, NonNull<U>, fn(), or NonZero* size_of::<T>() align_of::<T>() None is guaranteed to be encoded as all-zero bits (0)

Array and Slice Layout Equivalence

An array [T; N] and a slice [T] of length N have identical contiguous element layouts in memory: element i always sits at byte offset i * size_of::<T>() from element 0. Because size_of::<T>() is already a multiple of align_of::<T>(), arrays and slices never insert extra padding between elements.

fn main() {
    let matrix: [[u16; 3]; 2] = [[1, 2, 3], [4, 5, 6]];

    // SAFETY: `[[T; M]; N]` is guaranteed to have the exact same contiguous memory
    // layout as `[T; M * N]`.
    let flat: &[u16; 6] = unsafe { &*(&matrix as *const [[u16; 3]; 2] as *const [u16; 6]) };

    println!("matrix size: {} bytes", size_of_val(&matrix));
    println!("flat array:  {:?}", flat);
}
matrix size: 12 bytes
flat array:  [1, 2, 3, 4, 5, 6]

Non-Guaranteed Layouts to Avoid Assuming

  1. Field order in default structs and tuples: struct Pair(u32, u32) and (u32, u32) do not guarantee that .0 sits at offset 0 and .1 sits at offset 4. Use [u32; 2] or #[repr(C)] when offset order is required.
  2. Fat pointer field order: While &[T] is 16 bytes on 64-bit platforms, the language does not guarantee whether the data pointer is the first word and length is the second word, or vice versa. Always extract components using .as_ptr() and .len().
  3. Standard library container headers: Vec<T>, String, HashMap<K, V>, Rc<T>, and Arc<T> are default-represented (#[repr(Rust)]) structs whose internal field order is an implementation detail and must never be transmuted across FFI.