Foreign Function Interface (FFI)

#[repr(C)]

By default, Rust structs and enums use the unspecified #[repr(Rust)] layout representation, which allows the compiler to reorder fields to minimize padding and pack enum discriminants into unused bit niches. The #[repr(C)] attribute instructs rustc to lay out a struct, union, or enum using the exact field ordering, alignment, and padding rules of the target platform's C compiler so values can be passed safely across FFI boundaries.

Source Declaration:
  struct Packet { a: u8, b: u32, c: u16 }

`#[repr(Rust)]` (Default - Reordered to save 4 bytes):
  +--------+--------+--------+--------+--------+--------+--------+--------+
  |          b: u32 (4 B)             |  c: u16 (2 B)   | a (1B) | pad(1B)|  = 8 bytes
  +--------+--------+--------+--------+--------+--------+--------+--------+

`#[repr(C)]` (Preserves exact C source declaration order):
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  | a (1B) |      padding (3 B)       |          b: u32 (4 B)             |  c: u16 (2 B)   |  padding (2 B)  | = 12 bytes
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  Offset 0                            Offset 4                            Offset 8

Comparing Layout Representations for FFI

Attribute Target Items Layout Guarantee
#[repr(Rust)] (default) Structs, enums Compiler may reorder fields freely; triggers improper_ctypes_definitions warning if used in extern "C"!
#[repr(C)] Structs, unions, fieldless enums, enums with fields Matches platform C ABI struct/enum layout and field order (offset_of! matches C's offsetof).
#[repr(transparent)] Newtype structs/enums with exactly 1 non-ZST field Guaranteed to have the exact same ABI, size, alignment, and register passing as the single inner field!
#[repr(C, packed)] Structs Eliminates all alignment padding (align = 1); taking references to misaligned fields is UB.
#[repr(C, align(N))] Structs Forces minimum alignment to N bytes (must be a power of 2) while keeping C field order.
#[repr(u8)] / #[repr(i32)] Enums Fixes the integer discriminant representation of an enum to a specific primitive width.

Verifying Field Offsets with std::mem::offset_of!

Stabilized in Rust 1.77, the std::mem::offset_of! macro computes the exact byte offset of a struct field at compile time without needing to construct an instance or use unsafe pointer math:

use std::mem::{align_of, offset_of, size_of};

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CFrameHeader {
    pub version: u8,      // 1 byte at offset 0 (followed by 3 bytes of padding)
    pub payload_len: u32, // 4 bytes at offset 4
    pub flags: u16,       // 2 bytes at offset 8 (followed by 2 bytes of trailing padding)
}

/// A zero-cost newtype wrapper with identical FFI ABI to `u64`.
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HandleId(pub u64);

// Compile-time layout assertions guaranteeing C header compatibility:
const _: () = {
    assert!(size_of::<CFrameHeader>() == 12);
    assert!(align_of::<CFrameHeader>() == 4);
    assert!(offset_of!(CFrameHeader, version) == 0);
    assert!(offset_of!(CFrameHeader, payload_len) == 4);
    assert!(offset_of!(CFrameHeader, flags) == 8);
};

fn main() {
    println!(
        "CFrameHeader: size={}B, align={}B, offsets=({}, {}, {})",
        size_of::<CFrameHeader>(),
        align_of::<CFrameHeader>(),
        offset_of!(CFrameHeader, version),
        offset_of!(CFrameHeader, payload_len),
        offset_of!(CFrameHeader, flags),
    );
    println!(
        "HandleId (#[repr(transparent)]): size={}B == u64 size={}B",
        size_of::<HandleId>(),
        size_of::<u64>()
    );
}
CFrameHeader: size=12B, align=4B, offsets=(0, 4, 8)
HandleId (#[repr(transparent)]): size=8B == u64 size=8B

Opaque Structs Across FFI

When a C API uses an opaque struct pointer (typedef struct sqlite3 sqlite3;) whose fields are never exposed in the C header—or when you want to pass a pointer to a Rust struct (*mut MyRustEngine) to C without exposing its fields—you do not need #[repr(C)] on MyRustEngine if it is only ever passed behind a raw pointer (*mut MyRustEngine / *const MyRustEngine) and never dereferenced by C.

For importing an opaque C struct whose layout is unknown to Rust, represent it as a #[repr(C)] struct containing a zero-sized array and a phantom marker that forbids construction and unpinning:

use core::marker::{PhantomData, PhantomPinned};

#[repr(C)]
pub struct OpaqueCContext {
    _data: [u8; 0],
    _marker: PhantomData<(*mut u8, PhantomPinned)>,
}