Recently when I was learning Type Layout in Rust (Type layout - The Rust Reference), I saw that struct in Rust supports the #[repr(C)] directive, so I wanna to see the difference between the default(Rust) representation and C-like representation. Here comes the code:
I can understand the padding rules and the size of the structure under the C representation, but what confused me is the struct ACG1 under Rust representation, 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?
The padding in Rust structs is not only not defined to be anything, it is not even stable across compilations.
That said, type-layout internally uses memoffset crate to calculate the offsets. Which constructs a pointer of the type and uses that to calculate the offset. I suspect this introduces problems. But I cannot put down a finger where.... Edit: Look at the answer from Hyenou.
The memory layout of a struct is undefined by default to allow for compiler optimizations like field reordering, but it can be fixed with the repr attribute.
In other words, it's UB to rely on any observation of it.
Also, it seems the result you got from the type-layout is incorrect.
@RedDocMD@Hyeonu
Thank you both very much for your answers. What I actually want to know is: what rules does Rust's compiler use to lay out a structure, because my previous language was C++, so I'm trying to figure out something more fundamental; but when I check Rust's documentation, it seems that the richest explanation for the layout of a structure is that the layout of a structure is unstable and unobservable, so I'm a bit difficult to accept
I used type-layout because I didn't find the official tools, and it seems to me that Rust's compiler shouldn't hide this information from developers, or rather, should provide a way to observe it all the time, even if the result may be different each time
It does - that's what #[repr(C)] is for - when you need to depend on the struct layout.
When you don''t need to, you don't care and Rust does whatever it finds convenient and thinks will do the task.
The slippery slope you risk there is that people will start to depend on implementation details, often rationalizing it as "it's okay because this is just a toy library and I don't care if it breaks", or "I really need to do X, so I'll use unsafe to calculate layouts anyway".
To quote Hyrum's Law:
With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody.
By not providing any official tools for inspecting the layout of a #[repr(Rust)] type, you make it simpler for people to follow the correct path of using #[repr(C)].