Alignment Requirements
Every type T in Rust has an alignment requirement (align_of::<T>()), measured in bytes, which specifies that the memory address of any valid instance of T must be an exact multiple of align_of::<T>(). Every alignment value is guaranteed to be a power of two ($1, 2, 4, 8, 16, \dots$), and the size of any type size_of::<T>() is always an exact multiple of its alignment.
Memory Addresses: 0x1000 0x1001 0x1002 0x1003 0x1004 0x1005 0x1006 0x1007
| | | | | | | |
`u8` (align = 1): Valid Valid Valid Valid Valid Valid Valid Valid
`u16` (align = 2): Valid -- Valid -- Valid -- Valid --
`u32` (align = 4): Valid -- -- -- Valid -- -- --
`u64` (align = 8): Valid -- -- -- -- -- -- --
Hardware and Compiler Consequences of Misalignment
Why does alignment matter?
- CPU Hardware Faults & Penalties: On strict architectures (such as ARMv6-M Cortex-M0 or SPARC) and on x86_64 SIMD instructions (
movdqa), accessing a misaligned address triggers an immediate hardware alignment fault. Even when supported in hardware, a load that straddles two CPU cache lines requires two cache accesses. - Rust Reference Invariant (Immediate UB!): In Rust's abstract machine, creating a reference (
&Tor&mut T) or callingptr::read(ptr)on a misaligned pointer is unconditional Undefined Behavior, even if you never read or write through the reference! The compiler uses known alignment guarantees to optimize loops with SIMD instructions and pack niche tags into low pointer bits.
Safely Reading Misaligned Data (read_unaligned and align_to)
When parsing binary network packets or file buffers (&[u8] with alignment 1), casting a byte pointer bytes.as_ptr().cast::<u32>() and dereferencing it directly is Undefined Behavior if bytes is not 4-byte aligned.
Instead, use:
u32::from_le_bytes/from_be_bytes: Safe, endian-explicit conversion from[u8; 4].std::ptr::read_unaligned(ptr)/write_unaligned(ptr, val): Reads or writes a value at any byte address without requiring alignment.slice.align_to::<U>(): Splits a slice&[T]into an unaligned prefix&[T], a properly aligned middle slice&[U], and an unaligned suffix&[T].
use std::mem::{align_of, size_of};
use std::ptr;
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy)]
pub struct SimdBlock([f32; 4]);
#[repr(C, packed)]
pub struct WireHeader {
pub tag: u8,
pub length: u32, // Located at offset 1 (MISALIGNED for u32!)
}
fn main() {
println!(
"SimdBlock: size={}B, align={}B",
size_of::<SimdBlock>(),
align_of::<SimdBlock>()
);
println!(
"WireHeader (packed): size={}B, align={}B",
size_of::<WireHeader>(),
align_of::<WireHeader>()
);
let header = WireHeader {
tag: 0xAA,
length: 0x1234_5678,
};
// Taking `&header.length` on a `#[repr(packed)]` struct is rejected by rustc
// because `length` sits at offset 1 (unaligned for `u32`).
// Instead, use `&raw const header.length` with `ptr::read_unaligned`:
let len_ptr: *const u32 = &raw const header.length;
assert!(!len_ptr.is_aligned());
// SAFETY: `len_ptr` points to 4 initialized bytes inside `header`, and
// `read_unaligned` does not require 4-byte alignment.
let len_val = unsafe { ptr::read_unaligned(len_ptr) };
println!("Safely read unaligned packed field: {len_val:#x}");
}
SimdBlock: size=16B, align=16B
WireHeader (packed): size=5B, align=1B
Safely read unaligned packed field: 0x12345678
Checking Pointer Alignment at Runtime (ptr.is_aligned())
Stabilized in Rust 1.79, ptr.is_aligned() returns true if ptr's address is a multiple of align_of::<T>(), making runtime alignment assertions before unsafe pointer casts clean and readable.