I came up with an idea for how to handle borrowing fields of types where a reference to the actual struct member is unsafe due to not being aligned, not being encoded using the standard values, etc.:
My idea is to redefine borrowing fields to instead borrow a new variable loaded with the decoded/realigned value of the field, and, if the field was borrowed mutably, write the variable back when the borrowed lifetime ends.
This only works for field types that don't contain UnsafeCell or similar.
Recoding/repacking fields would be opt-in for structs by using a annotation (or equivalent) on the struct:
#[repack]
struct S {
a: bool,
b: bool,
c: u64,
}
So the struct S could take only 9 bytes and have an alignment of 1 because a and b would be encoded in the same byte and c would be stored unaligned.
The critical part is that all borrows of fields of a #[repack]ed struct/enum would be safe, since making temporary variables and reading/writing fields are safe.
(this is already how destructors for #[repr(packed)] types work FWIW, they copy the data out to an aligned stack slot and then invoke its destructor; the issue is that that only works as long as that slot still exists)
I vaguely recall some other proposals that attacked this problem slightly differently, but the proposal I remember the best is my own, so this is the one I am going to plug.
As for #[repr(packed)], I have been wondering a couple of times whether a wrapper type could work better than an attribute. Something like:
where Packed<T> is guaranteed to have an alignment of 1, but otherwise the same memory representation as T, so we could have unsafe fn assume_aligned(&Packed<T>) -> &T and fn get(&Packed<T>) -> T where T: Copy. To make it truly expedient we would have to add some kind of language feature that would enable struct member access to ‘factor through’ the wrapper. But we already need to solve that problem for Cell, MaybeUninit and other such wrappers, so… that just adds more justification to what is already a wanted feature.