首页 > 解决方案 > 为什么通过原始指针修改可变引用的值不会违反 Rust 的别名规则?

问题描述

我对 Rust 的别名规则没有特别扎实的理解(而且据我所知,它们并没有明确定义),但我无法理解是什么让文档中的代码示例std::slice正常。我会在这里重复一遍:

let x = &mut [1, 2, 4];
let x_ptr = x.as_mut_ptr();

unsafe {
    for i in 0..x.len() {
        *x_ptr.offset(i as isize) += 2;
    }
}
assert_eq!(x, &[3, 4, 6]);

我在这里看到的问题是x,作为&mut参考,编译器可以假定它是唯一的。xget 修改的内容通过x_ptr,然后读回 via x,我看不出为什么编译器不能仅仅假设x没有被修改,因为它从未通过唯一现有的&mut引用进行修改。

那么,我在这里缺少什么?

如果有某种稳定的规则可以使这个例子正常,它到底是什么?它的程度如何?我应该在多大程度上担心别名假设会破坏unsafeRust 代码中的随机事物?

标签: pointersrustunsafeborrowing

解决方案


免责声明:目前还没有正式的内存模型。1

首先,我想说明:

我在这里看到的问题是x,作为&mut参考,编译器可以假定它是唯一的。

是的……不。如果不借用x,只能假设是唯一的,一个重要的区别:

fn doit(x: &mut T) {
    let y = &mut *x;
    //  x is re-borrowed at this point.
}

因此,目前,我会假设从某种意义上派生指针x将暂时“借用” 。x

当然,在没有正式模型的情况下,这一切都是多余的,这也是 rustc 编译器在别名优化方面不太积极的部分原因:在定义正式模型并检查代码以匹配它之前,优化必须保守。

1 RustBelt 项目旨在为 Rust 建立一个经过正式验证的内存模型。来自 Ralf Jung 的最新消息是关于Stacked Borrows 模型的。


来自 Ralf(评论):上例中的关键点是有一个明确的从xtox_ptr和 back to转移x。因此,x_ptr从某种意义上说,这是一个范围借用。如果使用 go x, x_ptr, back tox和 back to x_ptr,那么后者将是未定义的行为:

fn main() {
    let x = &mut [1, 2, 4];
    let x_ptr = x.as_mut_ptr(); // x_ptr borrows the right to mutate

    unsafe {
        for i in 0..x.len() {
            *x_ptr.offset(i as isize) += 2; // Fine use of raw pointer.
        }
    }
    assert_eq!(x, &[3, 4, 6]);  // x is back in charge, x_ptr invalidated.

    unsafe { *x_ptr += 1; }     // BÄM! Used no-longer-valid raw pointer.
}

推荐阅读