我对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代码中的随机数?
答案 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(评论):上例中的重点是从x
到x_ptr
到再次回到x
的清晰转移。因此,x_ptr
在某种意义上是有范围的借用。如果用法变为x
,x_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.
}