We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Performance and Data Layout step 6 of 20
repr(C): computing struct layout by hand
Compute the size, alignment and field offsets of four supplied #[repr(C)]
structs. First work through the padding by hand, then use Rust's layout
queries to return the compiler's results.
pub fn layout() -> Vec<(usize, usize, Vec<usize>)>
Return exactly four tuples in the order A, B, C, D. Each contains
(size in bytes, alignment in bytes, field offsets in declaration order).
Keep the supplied struct definitions. Use std::mem::size_of, align_of
and offset_of! rather than replacing the starter's guesses with new literals.
The types and target assumptions
#[repr(C)] struct A { a: u8, b: u32, c: u8 }
#[repr(C)] struct B { inner: A, d: u16 }
#[repr(C)] struct C { a: u8, arr: [u16; 3], b: u8 }
#[repr(C)] struct D { flag: bool, count: u32, tag: u8, code: u16 }
This exercise's expected results assume alignment 1 for u8 and bool,
alignment 2 for u16, and alignment 4 for u32. Their sizes are 1, 1, 2
and 4 bytes respectively. The structs have no packed or explicit align
modifier. Layout is deterministic once the target's field sizes and
alignments, representation and declaration order are fixed.
repr(C) does not make every field's layout identical on all platforms.
When working on a different target, query that target's sizes and alignments
rather than assuming host results apply. It also does not recursively change
a nested type's representation: A has its own repr(C) declaration here.
Place fields, then round the total size
For these structs:
- Start at byte offset zero.
- In declaration order, round the current offset up to a multiple of the next field's alignment. An already aligned offset stays unchanged.
- Record that field's offset, then advance by its size.
- Use the largest field alignment as the struct's alignment.
- Round the final offset up to a multiple of the struct's alignment to get the total size.
The gaps between fields are interior padding. The final rounding may add trailing padding. The padded total size is also the stride between elements in an array, keeping each element aligned. Padding bytes are not field values and should not be assumed to contain zeros or be safe to serialize directly.
An array's alignment is its element alignment. Under these assumptions,
[u16; 3] occupies six bytes but needs alignment two, not six or eight.
Work through A
| Field | Alignment | Offset | End offset |
|---|---|---|---|
a: u8 |
1 |
0 |
1 |
b: u32 |
4 |
4 |
8 |
c: u8 |
1 |
8 |
9 |
Three padding bytes precede b. The largest alignment is four, so the final
offset nine rounds up to twelve. A therefore has size 12, alignment 4
and offsets [0, 4, 8].
For B, use all twelve bytes of A, including its trailing padding, before
placing d. For C, treat the array as one six-byte field with alignment
two. For D, place the final u16 at an aligned offset and include both of
its bytes before checking whether any trailing padding is needed.
The starter contains incorrect sizes, but not all errors have the same cause. Check each field's end offset as well as the final rounding step.
Ask the compiler
size_of::<A>() returns the full size including padding.
align_of::<A>() returns the required alignment. offset_of!(A, b) returns
the byte offset of b within A; unlike the first two queries, it is a macro.
The harness calls layout() and inspects one tuple at a time. It also checks
that the returned vector contains four tuples. Your function itself takes
no input and must return all four layouts on every call.
Why field order matters
For repr(C), declaration order is preserved. Placing more strongly aligned
fields earlier can reduce padding, but do not reorder these exercise structs
or an existing foreign ABI just to save space.
The default Rust representation may choose a different field order. It does
not promise a particular sorting algorithm or a layout stable across builds.
For FFI, layout is only part of compatibility: field validity, ownership and
the function calling convention also matter. For example, Rust's bool
accepts only the byte representations 0 and 1.
Check the computed tuples and address Clippy warnings. Passing requires compilation, correct layout results and the lint gate.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.