| An unaligned reference to a field of a [packed] `struct` or `union` was created. |
| |
| The `#[repr(packed)]` attribute removes padding between fields, which can |
| cause fields to be stored at unaligned memory addresses. Creating references |
| to such fields violates Rust's memory safety guarantees and can lead to |
| undefined behavior in optimized code. |
| |
| Erroneous code example: |
| |
| ```compile_fail,E0793 |
| #[repr(packed)] |
| pub struct Foo { |
| field1: u64, |
| field2: u8, |
| } |
| |
| unsafe { |
| let foo = Foo { field1: 0, field2: 0 }; |
| // Accessing the field directly is fine. |
| let val = foo.field1; |
| // A reference to a packed field causes a error. |
| let val = &foo.field1; // ERROR |
| // An implicit `&` is added in format strings, causing the same error. |
| println!("{}", foo.field1); // ERROR |
| } |
| ``` |
| |
| Creating a reference to an insufficiently aligned packed field is |
| [undefined behavior] and therefore disallowed. Using an `unsafe` block does not |
| change anything about this. Instead, the code should do a copy of the data in |
| the packed field or use raw pointers and unaligned accesses. |
| |
| ``` |
| #[repr(packed)] |
| pub struct Foo { |
| field1: u64, |
| field2: u8, |
| } |
| |
| unsafe { |
| let foo = Foo { field1: 0, field2: 0 }; |
| |
| // Instead of a reference, we can create a raw pointer... |
| let ptr = std::ptr::addr_of!(foo.field1); |
| // ... and then (crucially!) access it in an explicitly unaligned way. |
| let val = unsafe { ptr.read_unaligned() }; |
| // This would *NOT* be correct: |
| // let val = unsafe { *ptr }; // Undefined Behavior due to unaligned load! |
| |
| // For formatting, we can create a copy to avoid the direct reference. |
| let copy = foo.field1; |
| println!("{}", copy); |
| |
| // Creating a copy can be written in a single line with curly braces. |
| // (This is equivalent to the two lines above.) |
| println!("{}", { foo.field1 }); |
| |
| // A reference to a field that will always be sufficiently aligned is safe: |
| println!("{}", foo.field2); |
| } |
| ``` |
| |
| ### Unions |
| |
| Although creating a reference to a `union` field is `unsafe`, this error |
| will still be triggered if the referenced field is not sufficiently |
| aligned. Use `addr_of!` and raw pointers in the same way as for struct fields. |
| |
| ```compile_fail,E0793 |
| #[repr(packed)] |
| pub union Foo { |
| field1: u64, |
| field2: u8, |
| } |
| |
| unsafe { |
| let foo = Foo { field1: 0 }; |
| // Accessing the field directly is fine. |
| let val = foo.field1; |
| |
| // A reference to a packed union field causes an error. |
| let val = &foo.field1; // ERROR |
| } |
| ``` |
| |
| ### Additional information |
| |
| Note that this error is specifically about *references* to packed fields. |
| Direct by-value access of those fields is fine, since then the compiler has |
| enough information to generate the correct kind of access. |
| |
| See [issue #82523] for more information. |
| |
| [packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers |
| [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html |
| [issue #82523]: https://github.com/rust-lang/rust/issues/82523 |