Why does Rust disallow mutable aliasing?

前端 未结 3 1107
执念已碎
执念已碎 2020-12-16 02:30

Rust disallows this kind of code because it is unsafe:

fn main() {
    let mut i = 42;
    let ref_to_i_1 = unsafe { &mut *(&mut i as *mut i32) };
           


        
3条回答
  •  自闭症患者
    2020-12-16 02:50

    A really common pitfall in C++ programs, and even in Java programs, is modifying a collection while iterating over it, like this:

    for (it: collection) {
        if (predicate(*it)) {
            collection.remove(it);
        }
    }
    

    For C++ standard library collections, this causes undefined behaviour. Maybe the iteration will work until you get to the last entry, but the last entry will dereference a dangling pointer or read off the end of an array. Maybe the whole array underlying the collection will be relocated, and it'll fail immediately. Maybe it works most of the time but fails if a reallocation happens at the wrong time. In most Java standard collections, it's also undefined behaviour according to the language specification, but the collections tend to throw ConcurrentModificationException - a check which causes a runtime cost even when your code is correct. Neither language can detect the error during compilation.

    This is a common example of a data race caused by concurrency, even in a single-threaded environment. Concurrency doesn't just mean parallelism: it can also mean nested computation. In Rust, this kind of mistake is detected during compilation because the iterator has an immutable borrow of the collection, so you can't mutate the collection while the iterator is alive.

    An easier-to-understand but less common example is pointer aliasing when you pass multiple pointers (or references) to a function. A concrete example would be passing overlapping memory ranges to memcpy instead of memmove. Actually, Rust's memcpy equivalent is unsafe too, but that's because it takes pointers instead of references. The linked page shows how you can make a safe swap function using the guarantee that mutable references never alias.

    A more contrived example of reference aliasing is like this:

    int f(int *x, int *y) { return (*x)++ + (*y)++; }
    int i = 3;
    f(&i, &i); // result is undefined
    

    You couldn't write a function call like that in Rust because you'd have to take two mutable borrows of the same variable.

提交回复
热议问题