How to calculate cute alignment size


struct S(u32,u64,);

fn main() {


    
    println!("size of S: {}", std::mem::size_of::<S>());
    println!("align of S: {}", std::mem::align_of::<S>());
}

Output:

size of S: 16
align of S: 8

How alignment return 8.
How we calculate alignment size

I do not have an answer but would like to extend your question:
I tried out struct S(u64,u32) and was surprised to see that rustc also uses 16 bytes with an alignment of 8 for it.
I would have expected it to still have an alignment of 8, but only use 12 bytes of data. Is there a reason rustc does this?

I also tried compiling in release mode, but that made no difference for the toy example.

The size must be a multiple of the alignment, so you cannot have a size of 12 and an alignment of 8.

3 Likes

An alignment of repr(Rust) structs (i.e. the ones which are not repr(C), repr(transparent) or repr(packed) is AFAIK unspecified, but can't be smaller then the alignment of individual fields. That's also leads to an answer to the follow-up question:

Since u64 has alignment of 8, the whole struct must have alignment at least 8. And since the size is always a multiple of alignment (which is necessary so that the instances of struct can be laid out next to each other), the size must be at least 16.

Why is that so? I would assume that the main problem concerns how arrays compute there required size, but shouldn't the alignment be enough to take the max(size,align) as required space for each element?

Ah, I guess if the size would not be a multiple of the alignment, then pointer arithmetic would be a bit more complicated (they would need to use the max(size,align) computation to know where the next element starts instead of just using the size). I see that this makes sense when one wants to be compatible with C code and probable would break all unsafe code in existence, but I still do not see a reason why this needs to be the case.

It won't. Consider two S's laid out next to each other:

  • The first one is on some address (say x).
  • The second one must be, as you assume, at x + max(size, align) = x + 12.
  • However, if x % 8 == 0, then (x + 12) % 8 == 4, i.e. the second instance would be unaligned.
1 Like

Okay, I admit that max(size,align) is incorrect, but I still do not see why the required space for a type must concern itself with the representation inside an array.
The array could without any problems compute the smallest multiple of the alignment bigger or equal than the size and use that for each element.

Old (2015) discussion about separating size from "stride".

2 Likes

It's probably too breaking of a change now because there has stably been no stride vs size distinction since 1.0.0 and the documentation reflects this, so unsafe code can also reasonably rely it.

If the distinction did exist, anything dealing with slices would have to treat the last element special; for instance something like a memcpy would have to write stride_of() * (length - 1) + size_of() bytes (for non-zero lengths), because there might be another struct in what would be today the padding of the last element.

That is also a breaking change as the size of the DST [T] is well documented to be size_of() * length. The final element of a slice can't be assumed to have a larger actual-size than an instance of the type outside of a slice, because every instance of a type is already part of a slice of size 1, in some sense.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.