We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Performance and Data Layout step 5 of 20
Compare Rust and C struct layouts
Compare four pairs of structs with identical fields. Give one member of each pair C representation and leave the other with Rust's default representation, then measure their sizes and alignments.
pub fn compare() -> Vec<(usize, usize)>
Add #[repr(C)] to CSmall, CWide, CMixed and CStagger. Keep the
R* definitions unchanged, with no representation attribute. Preserve all
fields and their declaration order. Use size_of and align_of in the
supplied function to return eight (size, alignment) pairs in this order:
CSmall, RSmall, CWide, RWide, CMixed, RMixed, CStagger, RStagger
All measurements are in bytes. A name starting with C does not affect
layout; the attribute is what changes the representation.
Calculate the C layout
For these structs, which have no packing or explicit alignment modifier,
repr(C) places fields in declaration order. Each field starts at an offset
divisible by its alignment. The total size rounds up to a multiple of the
largest field alignment.
For { a: u8, b: u32, c: u8 } on a target with u32 alignment four:
| Bytes | Contents |
|---|---|
0 |
a |
1..=3 |
Interior padding |
4..=7 |
b |
8 |
c |
9..=11 |
Trailing padding |
The fields occupy six bytes and the padding occupies six more, for a total size of twelve. Trailing padding keeps successive array elements aligned.
Observe the default Rust layout
Rust's default representation can reorder fields. For example, placing the four-byte field first can reduce this struct's size from twelve to eight bytes. It is an optimization the compiler can make, not a field-sorting algorithm the language promises.
The following sizes are observations from Rust 1.98.1 on the verification
host, where the integer alignments are 1, 2, 4 and 8 for u8, u16, u32
and u64. They are useful comparisons, not portable constants for Rust
representation:
| Field types in declaration order | C size | Observed Rust size |
|---|---|---|
u8, u32, u8 |
12 |
8 |
u8, u64, u16 |
24 |
16 |
u16, u8, u16, u8 |
8 |
6 |
u8, u16, u8, u32 |
12 |
8 |
The reductions differ: the third pair saves two bytes per element, while the second saves eight. Multiply the measured size difference by the element count to estimate an array's storage difference.
Know what is guaranteed
Default Rust layout must align fields correctly and avoid overlapping the storage of nonzero-sized struct fields. The struct's alignment is at least that of its most strongly aligned field. These constraints do not promise:
- Declaration-order placement or any particular sorting algorithm.
- The smallest possible size.
- A size no larger than the corresponding C representation.
- Alignment exactly equal to that of the corresponding C representation.
- Identical layouts for separately declared types with matching fields.
The C results also depend on the target's primitive alignments. In particular,
the fixture for CWide assumes u64 alignment eight; some targets use a
smaller alignment. repr(C) defines a layout rule for the target rather
than one universal byte layout for every platform.
Interpret this exercise's checks
The harness checks eight returned pairs, pins each C size and alignment, and checks that the measured Rust size is no larger and its alignment matches. Those last two checks are observations expected by this exercise's compiler/target setup, not language guarantees. They may need review when changing the toolchain, target or layout-randomization settings.
Use the compiler queries as requested. Do not hardcode the observed Rust sizes to satisfy these checks. In production tests, distinguish a deliberate platform-specific requirement from a guarantee intended to hold on every supported compiler and target.
Apply layout information carefully
C representation can help match a foreign struct, but FFI also requires compatible field types, value validity, ownership and calling conventions. Do not assume default Rust layout matches a foreign declaration.
Neither representation is automatically a file or network format. Padding, byte order, pointers and valid-value constraints still need handling. Encode fields explicitly when a format must be portable.
For a C-layout type you control, changing field order can reduce padding. Once another component depends on that layout, reordering is an ABI change. Default Rust layout may already reduce padding, but measure the actual types rather than assuming every ordering or representation has the same cost.
Run the comparison and check the reported measurements. Passing requires compilation, the expected results for this exercise and the Clippy 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.