Description
This is a tracking issue for the future-incompatibility warning unaligned_references
.
This warning will fire for code like the following:
#[repr(packed)]
struct Foo1 {
bar: u8,
baz: usize
}
let foo = Foo1 { bar: 1, baz: 2 };
// Direct field accesses are fine.
let val = foo.baz;
// However, creating a reference is not.
let brw = &foo.baz; //~WARN reference to packed field is unaligned
// Format strings implicitly create references.
println!("{}", foo.baz); //~WARN reference to packed field is unaligned
The reason this pattern is being phased out is that Rust requires references to always be aligned; creating an unaligned reference falls under the "creating an invalid value" clause in the Rust definition of Undefined Behavior. Fields of packed structs are not necessarily properly aligned. Hence creating a reference to a field of a packed struct can cause UB, even if it is never used, and even inside an unsafe
block. This is a soundness bug, which is fixed by deprecating and eventually disallowing this pattern.
Previously, a future-incompatibility warning was emitted when creating references to packed fields outside an unsafe block; however, that warning was incorrectly silenced inside unsafe blocks.
To fix this code, it needs to stop creating a reference to a packed field. The alternative is to either just copy the packed field by adding curly braces (the compiler knows how to do that despite lack of alignment), or to create a raw pointer:
let mut foo = Foo1 { bar: 1, baz: 2 };
let brw = std::ptr::addr_of!(foo.baz); // Create an immutable raw pointer to the packed field.
let val = unsafe { brw.read_unaligned() }; // Perform an unaligned read of that pointer.
let brw_mut = std::ptr::addr_of_mut!(foo.baz); // Create a mutable raw pointer to the packed field.
unsafe { brw_mut.write_unaligned(val+1); } // Perform an unaligned write to that pointer.
// For formatting, adding curly braces means that a copy of the field is made, stored
// in a (properly aligned) temporary, and a reference to that temporary is being formatted.
println!("{}", {foo.baz});
// This is equivalent to the more verbose
println!("{}", {let copy = foo.baz; copy});