#[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)>,
}