Struct layout
- Rust course
- Expert
- Lesson 7/46
- 4 min
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 bytesIn 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.
Practice 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.
Starter code
fn main() {
// buraya yaz
}Sample 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>());
}Expected output
16 24
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 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 Correct answer
Explanation: 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.
2 Field offset
use std::mem::offset_of;
println!("{}", ____!(C, b)); // 8
use std::mem::offset_of;
println!("{}", offset_of!(C, b)); // 8
Explanation: offset_of! gives a field's byte distance from the start of the struct.
3 Definition and size
| Term | Meaning |
|---|---|
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) |