In this lesson, you will learn how to map nested C structures and unions into safe, idiomatic Rust representations without sacrificing performance or memory layout guarantees.
When dealing with legacy C APIs, you often encounter structures that contain anonymous unions or deeply nested padding. A common mistake is attempting to map these to standard Rust structs without considering the ABI.
Mapping Nested Structs
In Rust, repr(C) is your primary tool. It ensures that the memory layout matches the C compiler’s expectations, including field ordering and alignment. When you nest a struct, simply define it as its own repr(C) type and use it as a field:
#[repr(C)]
pub struct RawHeader {
pub id: u32,
pub flags: u16,
}
#[repr(C)]
pub struct ComplexPacket {
pub header: RawHeader,
pub payload_len: u16,
}
Handling Unions
C unions are "unsafe" by nature because they allow multiple fields to occupy the same memory. Rust provides #[repr(C)] unions, but they must be wrapped in an unsafe block to access their fields. To make this idiomatic, we often wrap the raw union in a safe Rust enum or provide getter/setter methods that handle the tagging logic (tracking which variant is currently active):
#[repr(C)]
pub union DataUnion {
pub int_val: i32,
pub float_val: f32,
}
pub struct SafeData {
pub raw: DataUnion,
pub is_float: bool,
}
impl SafeData {
pub fn get_value(&self) -> f32 {
if self.is_float {
unsafe { self.raw.float_val }
} else {
0.0 // Simplified: handle error or default
}
}
}
Alignment and Padding
If your C struct uses specific alignment pragmas (e.g., #pragma pack(1)), you must apply #[repr(C, packed)] in Rust. Be careful: packed can cause alignment issues on some CPU architectures if you attempt to reference a field directly, as the pointer might not be correctly aligned for its type. Always verify the size of your structures using std::mem::size_of::<T>() to ensure your Rust representation matches the C definition exactly.
Key Takeaway: Use #[repr(C)] to guarantee ABI compatibility and wrap raw unions in high-level abstractions that manage the safety invariants for you.