CodeBlanks Logo Icon CodeBlanks

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.

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

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
#![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

Question
use std::mem::offset_of;
println!("{}", ____!(C, b));   // 8
Answer
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)

All lessons in Expert