【发布时间】:2021-02-01 17:48:26
【问题描述】:
The Rust Reference 似乎说改变不可变的本地数据(不在UnsafeCell 内)是未定义的行为:
行为被视为未定义
- 改变不可变数据。 const 项内的所有数据都是不可变的。此外,通过共享引用获得的所有数据或不可变绑定拥有的数据都是不可变的,除非该数据包含在
UnsafeCell<U>中。
以下代码通过将不可变的局部变量重新解释为AtomicU32 来对其进行变异。目前代码运行良好并打印出预期的结果,但它的行为实际上是未定义的吗?
use std::sync::atomic::{AtomicU32, Ordering};
#[repr(C, align(4))]
struct Bytes([u8; 4]);
fn main() {
let bytes = Bytes([11; 4]);
let x = unsafe { &*(&bytes as *const Bytes as *const AtomicU32) };
x.store(12345, Ordering::SeqCst);
println!("{:?}", bytes.0); // [57, 48, 0, 0]
}
Miri 没有抱怨下面的代码示例中的字节是可变的。由于这些字节是通过共享引用 (&AtomicU32) 发生变异的,因此在我看来,根据 The Rust Reference,下面的代码也应该具有未定义的行为 - 鉴于 “所有数据通过共享引用到达 [..] 是不可变的” 和 “变异不可变数据 [被认为是未定义的行为]”。
use std::sync::atomic::{AtomicU32, Ordering};
#[repr(C, align(4))]
struct Bytes([u8; 4]);
fn main() {
let mut bytes = Bytes([11; 4]);
let x = unsafe { &*(&mut bytes as *mut Bytes as *const AtomicU32) };
x.store(12345, Ordering::SeqCst);
println!("{:?}", bytes.0); // [57, 48, 0, 0]
}
【问题讨论】:
-
您正在通过不可变引用
&bytes来改变bytes,您问题中的引用很清楚地表明这是未定义的行为。你还能期待什么? -
我只是在寻找对参考文本的确认。你对我的后续代码示例有什么想法吗?
-
后面的例子很好,因为
&AtomicU32是从&mut Bytes派生的。即使bytes本身被标记为可变,&*(&bytes as *const AtomicU32)也将是不健全的,因为指针的 provenance 对你可以用它做什么很重要。另请参阅Temporarily opt-in to shared mutation,使用Cell但不使用unsafe基本相同。 (不幸的是,Atomics 没有稳定的等价物)
标签: rust immutability undefined-behavior