The std::ptr::addr_of! macro serves a critical role in unsafe Rust by allowing the creation of raw pointers to fields without the intermediate step of creating a reference. When dealing with #[repr(packed)] structs, fields may reside at unaligned memory offsets, violating the alignment requirements inherent to reference types. Attempting to create a reference via the & operator to such unaligned data constitutes immediate undefined behavior, regardless of whether the reference is subsequently used. The addr_of! macro circumvents this by directly materializing a raw pointer from the field's address, bypassing the alignment and validity invariants enforced by references. This distinction is vital for sound FFI interactions and low-level memory manipulation where packed data layouts are prevalent.
While developing a high-performance parser for a legacy binary protocol, the engineering team encountered a #[repr(packed)] struct where a u32 field was intentionally placed at an offset of 1 byte to match an external hardware register map. The initial implementation attempted to borrow this field using &packet.status_register to pass to a validation function, unaware that this created an unaligned reference and triggered immediate undefined behavior.
The first solution considered involved removing the packed attribute and manually inserting padding bytes to force alignment. This approach guaranteed safety by allowing natural reference creation, but broke binary compatibility with the hardware specification and wasted memory bandwidth when transferring large arrays of these structures.
The second approach proposed using pointer arithmetic with unsafe { &*(base_ptr.add(1) as *const u32) } to calculate the field address manually. While this avoided the direct field access syntax, it still materializes a reference through the &* dereference operator, which constitutes undefined behavior if the resulting pointer is not properly aligned, offering no safety improvement over the original naive borrow and potentially misleading future maintainers.
The team ultimately selected the third solution, utilizing std::ptr::addr_of! to derive a raw pointer to the unaligned field without creating a reference intermediate. This pointer was then passed to std::ptr::read_unaligned to safely copy the value into a properly aligned local variable. This strategy preserved the required memory layout while strictly adhering to Rust's memory model, resulting in code that passed rigorous testing with Miri and functioned correctly across multiple target architectures including ARM and x86_64.
Why does creating a reference to unaligned data constitute undefined behavior even if the reference is immediately cast to a raw pointer?
In Rust, the act of creating a reference—such as &packed.field—is not merely a pointer calculation but an assertion to the compiler that the target memory satisfies all invariants of that reference type, including alignment and validity for reads. The LLVM backend and Rust's optimizer assume these invariants hold immediately upon reference creation, enabling aggressive optimizations like load-store reordering or speculative loads. Even if the reference is instantly cast to *const T, the optimizer may have already emitted instructions assuming aligned access, or it may mark the reference value as dereferenceable in LLVM metadata, leading to miscompilation on architectures with strict alignment requirements. Therefore, the undefined behavior occurs at the moment of reference creation, not at the point of dereference, making the mere existence of an unaligned reference toxic to program correctness.
How does addr_of! differ from using as *const _ on an existing reference, and why is the macro necessary?
When writing &packed.field as *const T, the Rust compiler first creates a reference (triggering alignment checks and potential UB) and only then converts that valid reference into a raw pointer. Conversely, std::ptr::addr_of! operates directly on the place expression (the field), generating a raw pointer without ever constructing a reference intermediate. This is crucial because the compiler treats the interior of addr_of! as a special construct that skips the reference validity checks, whereas the as keyword performs a value-to-value conversion that requires the source value (the reference) to be valid. Using the macro ensures that the pointer derivation itself cannot introduce undefined behavior due to alignment violations, providing the only sound path to obtain addresses of potentially unaligned data.
What additional considerations apply when using addr_of_mut! to obtain pointers to fields within a struct containing UnsafeCell?
When a #[repr(packed)] struct contains an UnsafeCell<T>, obtaining a mutable pointer to the interior requires careful handling of Rust's aliasing rules. The UnsafeCell provides interior mutability, but creating a mutable reference (&mut) to an unaligned UnsafeCell field still violates alignment requirements and is undefined behavior. Candidates often assume UnsafeCell somehow exempts the pointer from alignment rules, but it only exempts from the exclusive reference aliasing guarantee (noalias), not from alignment. Using addr_of_mut! yields a *mut T that must still respect the underlying type's alignment when eventually dereferenced or passed to UnsafeCell::raw_get, necessitating the use of read_unaligned or write_unaligned for actual data access.