rust - 为什么 Rust 不允许可变别名?

标签 rust undefined-behavior lifetime

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/

相关文章:

javascript - wasm可以使用sysinfo依赖项吗?

iterator - Rust struct 可以借用 "&' a mut self"两次,那么为什么不能借用 trait?

c++ - 在生产中使用 Address Sanitizer 或其他未定义的行为 sanitizer ?

c++ - 对象能否从标准 C++ 容器中删除自身?

c++ - 是否将指针转换为 const 指针并转换回未定义的原始类型?

c - 堆变量是全局变量,什么是堆变量的范围和生命周期

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 将非复制变量移动到异步闭包 : captured variable cannot escape `FnMut` closure body

types - Rust 中的默认整数类型是什么?

rust - 如何在 Rust 允许的函数内返回变量 "let"?