Rust 不允许这种代码,因为它不安全:
fn main() {
let mut i = 42;
let ref_to_i_1 = unsafe { &mut *(&mut i as *mut i32) };
let ref_to_i_2 = unsafe { &mut *(&mut i as *mut i32) };
*ref_to_i_1 = 1;
*ref_to_i_2 = 2;
}
如何使用对同一事物的多个可变引用来做坏事(例如段错误、未定义的行为等)?
我能看到的唯一可能的问题来自数据的生命周期。在这里,如果 i
还活着,那么对它的每个可变引用都应该没问题。
我知道引入线程时可能会出现问题,但是为什么即使我在一个线程中进行所有操作也无法阻止?
最佳答案
在 C++ 程序中,甚至在 Java 程序中,一个真正常见的陷阱是在迭代集合时修改它,如下所示:
for (it: collection) {
if (predicate(*it)) {
collection.remove(it);
}
}
对于 C++ 标准库集合,这会导致未定义的行为。也许迭代会一直工作到你到达最后一个条目,但最后一个条目将取消引用一个悬空指针或读取数组的末尾。也许集合下的整个数组将被重新定位,并且它会立即失败。也许它大部分时间都有效,但如果重新分配发生在错误的时间,它就会失败。在大多数 Java 标准集合中,根据语言规范,它也是未定义的行为,但集合往往会抛出 ConcurrentModificationException
- 即使您的代码正确,该检查也会导致运行时成本。两种语言都无法在编译期间检测到错误。
这是由并发引起的数据竞争的常见示例,即使在单线程环境中也是如此。并发不仅仅意味着并行性:它还意味着嵌套计算。在 Rust 中,这种错误会在编译期间被检测到,因为迭代器具有对集合的不可变借用,因此您不能在迭代器处于事件状态时改变集合。
一个更容易理解但不太常见的例子是当您将多个指针(或引用)传递给一个函数时的指针别名。一个具体的例子是将重叠的内存范围传递给 memcpy
而不是 memmove
。实际上,Rust's memcpy
equivalent 不安全
也是,但那是因为它采用指针而不是引用。链接页面显示了如何使用可变引用 永不别名的保证来创建安全的交换 函数。
引用别名的一个更人为的例子是这样的:
int f(int *x, int *y) { return (*x)++ + (*y)++; }
int i = 3;
f(&i, &i); // result is undefined
你不能在 Rust 中编写这样的函数调用,因为你必须对同一个变量进行两次可变借用。
关于rust - 为什么 Rust 不允许可变别名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49174630/