rust 在释放模式下,从不可变上下文进行不安全的变异失败

zbq4xfa0  于 2023-04-12  发布在  其他
关注(0)|答案(1)|浏览(129)

在下面的代码中,我使用不安全代码将不可变引用转换为可变指针,然后尝试通过此可变指针编辑内部值。

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.我想做的事情在--释放模式下是不可能的吗?

idfiyjo8

idfiyjo81#

Rust不允许你改变一个从不可变的引用中获取的值,即使你把它转换成一个可变的引用或可变的原始指针。编译器被允许假设一个使用不可变的引用传递的值永远不会改变,它可以在考虑到这一点的情况下进行优化。打破这个假设是未定义的行为,并且可能会以非常意外的方式破坏你的程序。这可能就是你的例子中发生的事情:编译器发现increment接受了一个不可变的引用,因此item在调用之前和之后必须是相同的,它可以优化代码,就像是这样。
只有一个例外:UnsafeCell.UnsafeCell是一个特殊的类型,它允许内部可变性,让您从&UnsafeCell<T>(使用不安全的代码)获得&mut T,并告诉编译器即使您对单元格有不可变的引用,它也不能假设内容不变。
但是UnsafeCell的问题是,您仍然需要维护Rust的借款担保(例如,可变引用在存在时必须是唯一的),但编译器将依赖于您作为程序员来确保这些被支持。因此,通常建议您使用安全类型,例如CellRefCellMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutexMutex,x1RwLockatomic types。这些都有不同的权衡,但它们都是围绕UnsafeCell构建的安全 Package 器,以允许内部可变性,同时对借用保证进行一些检查(一些在编译时,一些在运行时)。
例如,Cell可以用于您的示例:

fn main() {
    #[repr(transparent)]
    struct Item(Cell<isize>);

    impl Item {
        fn increment(&self) {
            let val = self.0.get() + 1;
            self.0.set(val);
        }
    }

    let item = Item(Cell::new(22));
    println!("before = {}", item.0.get());
    item.increment();
    println!("after = {}", item.0.get());
}

相关问题