在下面的代码中,我使用不安全代码将不可变引用转换为可变指针,然后尝试通过此可变指针编辑内部值。
fn main() {
#[repr(transparent)]
struct Item(isize);
impl Item {
#[inline]
fn ptr(&self) -> *mut isize {
self as *const Item as *mut isize
}
fn increment(&self) {
let val = self.0 + 1;
unsafe {std::ptr::write(self.ptr(), val)}
}
}
let item = Item(22);
println!("before = {}", item.0);
item.increment();
println!("after = {}", item.0);
}
当我在调试模式下编译它时,结果和预期的一样,值确实增加了。然而,在发布模式下,尽管代码的那一部分运行,值根本没有增加。此外,以下其他类型的值变化似乎也不起作用unsafe {*&mut *(self.ptr()) += 1;}
std::mem::replace()
std::mem::swap()
1.这是什么原因呢?
1.我想做的事情在--释放模式下是不可能的吗?
1条答案
按热度按时间idfiyjo81#
Rust不允许你改变一个从不可变的引用中获取的值,即使你把它转换成一个可变的引用或可变的原始指针。编译器被允许假设一个使用不可变的引用传递的值永远不会改变,它可以在考虑到这一点的情况下进行优化。打破这个假设是未定义的行为,并且可能会以非常意外的方式破坏你的程序。这可能就是你的例子中发生的事情:编译器发现
increment
接受了一个不可变的引用,因此item
在调用之前和之后必须是相同的,它可以优化代码,就像是这样。只有一个例外:
UnsafeCell
.UnsafeCell
是一个特殊的类型,它允许内部可变性,让您从&UnsafeCell<T>
(使用不安全的代码)获得&mut T
,并告诉编译器即使您对单元格有不可变的引用,它也不能假设内容不变。但是
UnsafeCell
的问题是,您仍然需要维护Rust的借款担保(例如,可变引用在存在时必须是唯一的),但编译器将依赖于您作为程序员来确保这些被支持。因此,通常建议您使用安全类型,例如Cell
,RefCell
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,Mutex
,x1RwLock
或atomic types。这些都有不同的权衡,但它们都是围绕UnsafeCell
构建的安全 Package 器,以允许内部可变性,同时对借用保证进行一些检查(一些在编译时,一些在运行时)。例如,
Cell
可以用于您的示例: