In the spirit of being charitable, I would say that the article is highlighting a place where the guarantees are incompletely documented. And not having a solid specification and clear documentation is absolutely one of the things that makes unsafe Rust hard.
I'd actually like to qualify that a bit. Doing unsafe is not especially hard, just use pointers everywhere instead of references. That's exactly like C (which doesn't even have references), but the syntax is clunkier, you have to use function calls instead of concise operators like * and ->. What is hard is finely interleaving safe and unsafe Rust, as the author is trying to do. That's difficult because the unsafe code has to upload all the safety invariants of safe Rust, and those are indeed complicated.
You are being nice, but even if there is a documentation error, we can prove that if safe rust isn't completely broken, and `& x.field` is allowed in safe Rust, then fields must be aligned. It is just preposterous Rust would be more broken than C in this regard.
> but the syntax is clunkier, you have to use function calls instead of concise operators like * and ->.
Yes I agree, the syntax does suck. I see the macros use an unstable &raw, that would be more concise.
I think would be really good is if x->y in Rust matched &x->y in C. That is nicely orthogonal to dereferencing, and always safe.
There could be some loophole. If the program globally never makes a reference to a field, must it be aligned? If the field is referenced, could it be that when making a reference, the field gets copied or moved out and a reference is implemented as a pointer to that copy?
> If the program globally never makes a reference to a field, must it be aligned?
That would be whole-progam analysis which isn't done.
> If the field is referenced, could it be that when making a reference, the field gets copied or moved out and a reference is implemented as a pointer to that copy?
This breaks down with mutation, especially atomics, which are allowed as struct fields.
> That would be whole-progam analysis which isn't done.
That doesn’t matter.
> This breaks down with mutation, especially atomics, which are allowed as struct fields.
Not true at all. First, the compiler could refrain from doing this sort of code generation on fields with atomics (or any kind of mutable cell). Second, the object could be misaligned in some local contexts sometimes but references passed to functions could be always properly aligned. So a RefCell or atomic could still be copied or moved out and mutated, then moved back in, when the object is known not to have any references.
In general, this is close to reasonable code generation — a local struct could have some of its parts in registers. Its parts might be in noncontiguous stack locations. A language spec needs to specify that a field can be referenced or accessed via its pointer. It might also distinguish this from an alignment guarantee.
If the compiler is so omnicient, then you should just be able to take the pointer to the field in the unsafe struct too!
Surely such a genius compiler can see "oh, they took a field reference, better make sure that field is aligned" whether the reference is a safe one or not!
I agree, but IMO it's not all that clear to me that this is guaranteed. The documentation for the default layout pretty clearly says[0]:
> There are no guarantees of data layout made by this representation.
So that being the case, there's not really anything stopping them from introducing a situation where an unaligned field in a struct is created in the future. Of course I can't imagine why they would do that, but then maybe my imagination just isn't good enough. I think the author's point here (which is a good one in my opinion) is that when writing `unsafe` you're not supposed to rely on stuff that seems like it should be true, you're supposed to rely on stuff that's guaranteed to always be true, which with Rust isn't all that clearly defined.
If struct fields weren't aligned, then safe Rust would be completely and utterly broken. And really obviously so.
This isn't just a matter of things "seeming". It is quite literally an implication of Safe Rust works => field offsets must be aligned. There is no other way for safe Rust to be safe, other than alignment not mattering at all because all accesses are careful to pessimistically not rely on it.
I am sorry, but this hypothesis is just completely outside the Overton Window.
The thing is, C has an actual standard and people get bit making the same kinds of assumptions on things not stated. You may very well be right that there's no way this will ever happen, but then it also doesn't appear to be guaranteed. They literally say they make no guarantees about the data layout, but you're assuming there is a guarantee about the data layout :P
Maybe this time it works out, but there's plenty of other cases where things are not so clear cut.
I'd actually like to qualify that a bit. Doing unsafe is not especially hard, just use pointers everywhere instead of references. That's exactly like C (which doesn't even have references), but the syntax is clunkier, you have to use function calls instead of concise operators like * and ->. What is hard is finely interleaving safe and unsafe Rust, as the author is trying to do. That's difficult because the unsafe code has to upload all the safety invariants of safe Rust, and those are indeed complicated.