想象一下,您正试图编写一个围绕一些可变静态变量的安全包装器:
#![feature(strict_provenance)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
struct Wrapper<T>(*mut T);
struct Guard<T>(*mut T);
impl<T> Wrapper<T> {
fn guard(&self) -> Guard<T> {
Guard(self.0)
}
}
// imagine there's synchronization going on that makes this all safe
unsafe impl<T: Send + Sync> Sync for Wrapper<T> {}
impl<T> std::ops::Deref for Guard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
impl<T> std::ops::DerefMut for Guard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
}
static mut GLOBAL_SINGLETON: isize = 0; // Imagine this couldn't be trivially replaced by an atomic type
static WRAPPER: Wrapper<isize> = Wrapper(unsafe { &GLOBAL_SINGLETON as *const _ as *mut _ });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Is this sound in this sepcific instance?
// Actually dereference the raw pointer to provoke UB
fn main() {
assert_eq!(0, *WRAPPER.guard());
*WRAPPER.guard() += 1;
assert_eq!(1, *WRAPPER.guard());
}
现在,通常情况下,强制转换&T as *const T as *mut T
并写入结果原始指针是不合理的,但严格来源和MIRI都不会抱怨上面的代码.这是由于堆叠借款和/或MILI的限制而导致的假阴性吗?还是会有static mut
件商品在全局被贴上SharedRW
件的标签,并在这里获得免费通行证?Ctrl-F代表Stacked Borrows paper中的"静态"没有显示任何相关信息.
我也考虑过这样的事情,我更喜欢这样,因为它迫使T
比Wrapper
活得更长.
struct Wrapper<'a, T>(&'a std::cell::UnsafeCell<T>);
impl<'a, T> Wrapper<'a, T> {
const fn new(t: &'a T) -> Self {
Self(unsafe { std::mem::transmute(t) }
}
}
// ...
static mut GLOBAL_SINGLETON: isize = 0;
static WRAPPER: Wrapper<'_, isize> = Wrapper::new(&GLOBAL_SINGLETON);
// ...
但这显然是不合理的(Miri会告诉你),因为它需要&GLOBAL_SINGLETON
个才能活着,而&UnsafeCell { GLOBAL_SINGLETON }
个人才能活着.