为什么通过原始指针修改可变引用的值不会违反Rust的别名规则?

时间:2018-09-28 04:11:37

标签: pointers rust unsafe borrowing

我对Rust的别名规则没有特别扎实的理解(从我听来并没有明确定义),但是我在理解{{{ 1}}文档还可以。我将在这里重复:

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的内容通过x进行了修改,然后通过x_ptr进行了读取,我认为编译器不能仅仅假设x并没有理由已被修改,因为从未通过仅有的x参考进行修改。

那么,我在这里想念什么?

  • 即使通常允许假定&mut 从不别名另一个{ {1}}?

  • *mut T块是否充当某种混叠屏障,编译器假设其中的代码可能已修改范围内的任何内容?

  • 此代码示例是否已损坏?

如果有某种稳定的规则可以使该示例正常运行,那到底是什么?程度如何?我应该担心多少假名假设会破坏&mut T Rust代码中的随机数?

1 个答案:

答案 0 :(得分:8)

免责声明:尚无正式的内存模型。 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 model


来自Ralf(评论):上例中的重点是从xx_ptr到再次回到x的清晰转移。因此,x_ptr在某种意义上是有范围的借用。如果用法变为xx_ptr,回到x再回到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.
}