为什么将 &T 转换为 &mut T 未定义行为?

Kap*_*chu 1 rust

我想重新解释对可变引用的不可变引用(在不安全的块中)并自行负责安全检查,但似乎我不能mem::transmute()这样做。

let map_of_vecs: HashMap<usize, Vec<_>> = ...;
let vec = map_of_vecs[2];
/// obtain a mutable reference to vec here
Run Code Online (Sandbox Code Playgroud)
  • 我不想将Vecs包装到Cells 中,因为这会影响使用的所有其他代码区域,map_of_vecs我只需要一行中的可变性。
  • 我没有可变访问权限 map_of_vecs

Pet*_*all 6

Rust 优化器假设&mut T引用是唯一的。例如,它可能会推断出可以重用特定内存块,因为对该内存的可变引用存在但永远不会再次访问。

但是,如果您将 a 转换&T为 a,&mut T则您可以创建对同一数据的多个可变引用。如果编译器做出这个假设,你最终可能会取消引用一个已经被其他东西覆盖的值。

这是编译器如何只是一个例子可以利用的前提是可变的引用是唯一的。事实上,编译器可以以任何它认为合适的方式自由使用这些信息——这可能(并且很可能会)从版本到版本发生变化。

即使您认为您已经保证引用没有别名,您也不​​能总是保证您的代码的用户不会创建更多引用。即使您认为可以确定这一点,引用的存在也是极其微妙的,而且很容易错过。例如,当您调用一个带有 的方法时&self,它就是一个引用。


Sve*_*ach 6

Rust 编译器&T使用 LLVMnoaliasreadonly属性来注释函数参数(前提是T不包含任何UnsafeCell部分)。该noalias属性告诉 LLVM 该指针后面的内存只能通过指针(而不能通过任何其他指针)写入,并且该readonly属性告诉 LLVM 不能通过该指针(但可能是其他指针)写入它。结合起来,这两个属性允许 LLVM 优化器假设在该函数执行期间内存根本没有改变,并且可以基于该假设来优化代码。优化器可能会重新排序指令或删除代码,只有在您真正遵守此合同时才安全。

转换可能导致未定义行为的另一种方式是对于静态:不带UnsafeCells 的不可变静态将被放入只读内存中,因此如果您实际写入它们,您的代码将出现段错误。

对于带有UnsafeCells 的参数,编译器不会发出该readonly属性,并且包含 an 的静态变量UnsafeCell会被放入可写内存中。