Description
I tried this code:
#[repr(C)]
union Foo {
x: u8,
y: u32,
}
impl Foo {
fn zero_first_byte(&mut self) {
let p = &raw mut self.x;
unsafe { *p = 0 }
}
}
I expected to see this happen: &raw mut self.x
should be a safe operation, because it consists only of in-bounds pointer arithmetic and does not construct a reference or value to uninit. An equivalent using offset_of!
is safe:
// wrapping_add is used because the compiler's knowledge of the field offset being in-bounds is lost.
let p: *mut u8 = ptr::from_mut(self).wrapping_byte_add(mem::offset_of!(Self, x)).cast();
Instead, this happened:
error[E0133]: access to union field is unsafe and requires unsafe block
--> src/lib.rs:12:26
|
12 | let p = &raw mut self.x;
| ^^^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
This indicates that Rust is blanket-banning access to the place self.x
without consideration for how that place is used. This seems to be an oversight in adding ptr::addr_of!
and the &raw
operation. It is unsafe
to construct a reference or move from such a place, but it should be safe to spell the place and get a raw pointer to it.
This should instead act more like access to packed fields: spelling packed_val.unaligned_field
only errors when constructing a reference to that field - merely spelling the place, moving, or using &raw const packed_val.unaligned_field
is accepted in safe code.
Meta
rustc --version --verbose
:
rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: x86_64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1
Occurs on current stable, beta, and nightly (2025-05-18 4d051fb306e661654d08
).
No backtrace was emitted.