'Struct padding rules in Rust

Recently when I was learning Type Layout in Rust, I saw that struct in Rust supports the #[repr(C)] directive, so I want to to see the difference between the default (Rust) representation and C-like representation. Here comes the code:

use type_layout::TypeLayout;

#[derive(TypeLayout)]
struct ACG1 {
    time1: u16, // 2
    time2: u16, // 2
    upper: u32, // 4
    lower: u16, // 2
}

#[derive(TypeLayout)]
#[repr(C)]
struct ACG2 {
    time1: u16, // 2
    time2: u16, // 2
    upper: u32, // 4
    lower: u16, // 2
}

fn main() {
    println!("ACG1: {}", ACG1::type_layout());
    println!("ACG2: {}", ACG2::type_layout());
}

And I get the following output:

Enter image description here

I understand the rules for padding the #[repr(C)] structure and the size of the structure as a whole, but what confused me is the Rust representation struct ACG1. I can't find any clear documentation on Rust padding rules, and I think the padding size should also be included in the overall size of the structure, but why is the size of ACG1 only 12 bytes?

BTW, this is the crate I used to assist in printing the layout of the structure: type-layout 0.2.0



Solution 1:[1]

This crate does not seem to account for field reordering. It appears the compiler reordered the struct to have upper first:

struct ACG1 {
    upper: u32,
    time1: u16,
    time2: u16,
    lower: u16,
}

It’s somewhat hard to see, but the derive macro implementation checks the difference between fields in declared order. So in this sense, there are four bytes of "padding" between the beginning of the struct and the first field (time1) and four bytes of "padding" between the third field (upper) and fourth field (lower).

There is an issue filed that it doesn't work for non-#[repr(C)] structs, so I would not recommend using this crate for this purpose.

As far as Rust's rules go, the reference says "There are no guarantees of data layout made by [the default] representation." So in theory, the compiler can do whatever it wants and reorder fields based on access patterns. But in practice, I don't think it’s that elaborate and organizing by field size is a simple way to minimize padding.

Solution 2:[2]

As others have said, it seem to be an issue in the crate. Better ask the compiler:

cargo clean
cargo rustc -- -Zprint-type-sizes

This will give you:

...
print-type-size type: `ACG1`: 12 bytes, alignment: 4 bytes
print-type-size     field `.upper`: 4 bytes
print-type-size     field `.time1`: 2 bytes
print-type-size     field `.time2`: 2 bytes
print-type-size     field `.lower`: 2 bytes
print-type-size     end padding: 2 bytes
print-type-size type: `ACG2`: 12 bytes, alignment: 4 bytes
print-type-size     field `.time1`: 2 bytes
print-type-size     field `.time2`: 2 bytes
print-type-size     field `.upper`: 4 bytes
print-type-size     field `.lower`: 2 bytes
print-type-size     end padding: 2 bytes

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Peter Mortensen
Solution 2 Peter Mortensen