Course contentsShow
← Rust
Lesson 1399 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Memory Layout Stability

You will leverage explicit struct layout attributes to guarantee deterministic memory maps for FFI communication.

In this lesson, you will learn how to use explicit struct layout attributes to guarantee that your data structures maintain a deterministic memory map, which is essential for safe and reliable FFI (Foreign Function Interface) communication.

The Problem with Default Layouts

By default, the Rust compiler is free to reorder the fields within your structs to minimize memory usage (padding). While this is great for performance, it is disastrous when you need to send a struct directly to a C library or map it to hardware registers. Because C expects a rigid, predictable order, Rust’s "optimization" breaks the contract between your code and the external system.

Enforcing Determinism with repr

To stabilize your memory layout, you must explicitly opt out of the default behavior using the #[repr(C)] attribute. This tells the compiler to lay out the fields exactly as they appear in your source code, following the ABI (Application Binary Interface) rules of the C programming language.

Consider this example of a packet header intended for a network device:

#[repr(C)]
struct NetworkPacket {
    version: u8,
    flags: u8,
    payload_len: u16,
    checksum: u32,
}

By applying #[repr(C)], you guarantee that version is at offset 0, flags is at offset 1, and so on. Without this attribute, the compiler might shuffle these fields or insert unpredictable padding to align the 32-bit checksum field, which would cause the external device to misinterpret your packet.

When to Use This

You should use #[repr(C)] whenever a struct crosses the "boundary" of the Rust abstract machine. This includes:

  1. FFI: Passing structs to or from C/C++ libraries.
  2. Serialization: Casting raw byte buffers (using unsafe pointer arithmetic) directly into struct references.
  3. Memory Mapped I/O: Interacting with hardware registers where the address of every bit is fixed by the device specification.

By locking the layout, you sacrifice the compiler's ability to optimize for space, but you gain the stability required for low-level, cross-language interoperability.

Key Takeaway: Always use #[repr(C)] for structs that interface with non-Rust code to ensure field ordering remains fixed and predictable.