rust-lang / unsafe-code-guidelines

Forum for discussion about what unsafe code can and can't do
https://rust-lang.github.io/unsafe-code-guidelines
Apache License 2.0
661 stars 57 forks source link

Do default repr structs have a contiguous memory representation? #431

Open DeltaF1 opened 1 year ago

DeltaF1 commented 1 year ago

This feels like a very silly/basic question to ask, but I'm getting hung up on "There are no other guarantees of data layout made by [the default] representation." Does Rust guarantee that turning a (Sized) default repr struct into an array of bytes (of length std::mem::size_of::<T>()), moving those bytes somewhere else in memory without modifying them, and then transmuting back into the original type will be correct? Assume that the new location is properly aligned to whatever std::mem::align_of::<T>() says it should be, and the struct doesn't contain anything exotic like a self-referential pointer.

I think https://github.com/rust-lang/unsafe-code-guidelines/issues/97 is related to this question (and especially the hypothetical Array-of-Structs to Struct-of-Arrays transformation). I think I'm asking the question "what is a struct", and how much is the compiler allowed to mess with the common intuition of a struct as a group of self-contained contiguous bytes as long as the normal safe accesses to that struct produce the expected results.

chorman0773 commented 1 year ago

I believe it does, yes.

I mean, the compiler is allowed to explode a struct into several independent scalar locations, provided the program doesn't observe the difference, and the result behaves as-if it didn't. (But this is a general rule: The compiler can do what it wants to implement your program, provided it resuls in the same observable behaviour as-if it executed it exactly as written).

CAD97 commented 1 year ago

struct into an array of bytes

Important caveat: transmuting to and copying specifically as [u8; SIZE] is no good (because of padding and mumbles mumbles provenance). Doing an untyped copy with ptr::copy[_nonoverlapping] is essentially guaranteed to work. Copying as MaybeUninit<[u8; SIZE]> or [MaybeUninit<u8>; SIZE] is not strictly guaranteed to work yet, but does under even the strictest memory models considered so far.