Rust
Struct layout
Learn how struct fields are laid out in memory.
Struct layout
In the default (repr(Rust)) layout, the compiler may change the order of fields; its goal is minimizing padding. It usually orders fields by alignment from largest to smallest. This layout isn't a documented guarantee; the same definition may be laid out differently by different compiler versions:
struct Rust { a: u8, b: u64, c: u16 } // reordered: 16 bytes
#[repr(C)]
struct C { a: u8, b: u64, c: u16 } // in written order: 24 bytes
In repr(C): a (1) + 7 padding + b (8) + c (2) + 6 padding = 24. In the Rust layout: b (8) + c (2) + a (1) + 5 padding = 16.
Observing the layout
offset_of! shows the layout the compiler chose; -Zprint-type-sizes (nightly) prints the detailed layout of all types:
use std::mem::offset_of;
println!("{}", offset_of!(C, b)); // 8
println!("{}", offset_of!(C, c)); // 16
println!("{}", offset_of!(Rust, b)); // usually 0 (not guaranteed)
Code that relies on the layout (FFI, binary file formats, transmute) must use repr(C) or another explicit repr.
Exercise Two layouts
Define a plain struct Rust and a #[repr(C)] struct C with the same fields (a: u8, b: u64, c: u16). Print the size_of of both separated by a space.
Show example solution
#![allow(dead_code)]
use std::mem::size_of;
struct Rust {
a: u8,
b: u64,
c: u16,
}
#[repr(C)]
struct C {
a: u8,
b: u64,
c: u16,
}
fn main() {
println!("{} {}", size_of::<Rust>(), size_of::<C>());
}
Test yourself
-
Why is a struct's size rounded up to a multiple of its largest alignment?
- Randomly for speed
- It isn't rounded
- The OS requires it
- So that every element in an array is aligned correctly too
Show answer
So that every element in an array is aligned correctly too — In [T; N] elements sit back to back; if size_of::<T>() weren't a multiple of the alignment, the second element would be misaligned.
-
Field offset
use std::mem::offset_of;
println!("{}", ____!(C, b)); // 8
Show answer
use std::mem::offset_of;
println!("{}", offset_of!(C, b)); // 8
-
Definition and size
- Rust { a: u8, b: u64, c: u16 }
- 16
- #[repr(C)] C { a: u8, b: u64, c: u16 }
- 24
- offset_of!(C, b)
- 8 (offset)
- (u8, u64)
- 16 (tuple)
Next lesson: #[repr(C)] and #[repr(Rust)]