pointers - 为什么通过原始指针修改可变引用的值不会违反 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
参考,编译器可以假定它是唯一的。x
get 修改的内容通过x_ptr
,然后读回 via x
,我看不出为什么编译器不能仅仅假设x
没有被修改,因为它从未通过唯一现有的&mut
引用进行修改。
那么,我在这里缺少什么?
编译器是否需要假设
*mut T
may alias&mut T
,即使通常允许假设&mut T
从不给另一个别名&mut T
?该
unsafe
块是否充当某种别名屏障,编译器假定其中的代码可能已经修改了范围内的任何内容?此代码示例是否损坏?
如果有某种稳定的规则可以使这个例子正常,它到底是什么?它的程度如何?我应该在多大程度上担心别名假设会破坏unsafe
Rust 代码中的随机事物?
解决方案
免责声明:目前还没有正式的内存模型。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(评论):上例中的关键点是有一个明确的从x
tox_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.
}
推荐阅读
- java - 在连续 8 次测量到的 GC 抖动后关闭 JVM
- java - 在片段中的 AsyncTask 中使用 Jsoup 获取网页元素(Android 新手)
- tensorflow - 如何在 Cleverhans 中导出 Facenet 的对抗样本?
- json - VUE.JS 通过 webpack-simple 中的 axios 从 url 接收一个 json 文件
- struct - 如何在solidity中获取特定地址的映射中创建的结构的概述?
- javascript - 检测左键单击并将其更改为中键 o Ctrl+单击
- c# - 将 DateTable 导出到 Excel 时无法获得提及的时间格式
- c++ - 代码未执行的情况 2。有人可以告诉我所需的更改吗
- corda - 运行单元测试时出现无包存在错误
- python - VS Code:在后台进行分析