Borrowing and Aliasing

Data can be “borrowed” as references, either immutably &T or mutably &mut T. The compiler enforces a sort of reader-writer lock on the type: it can have either multiple readers (immutable/shared references, &T) or a singular writer (mutable/exclusive reference, &mut T). The compiler will assume that the data behind a shared reference will not mutate (unless the type opts out of it with UnsafeCell, which can be used for custom special types, which should not be used to enforce users’ types) and the compiler will assume that no other code or data can reference, read, or mutate the data behind an exclusive reference (there is no opt out, this must never happen!). The fact that Rust can make these assumptions is what makes it so fast and efficient, but it also means you are restricted from coding practices that break them.

This is approximately the exact opposite of Java’s memory model, where everything is a mutable reference to the underlying object. While Java can’t arbitrarily clone objects, meaning it can’t make copies of a class holding an exclusive reference, it can make those objects live arbitrarily long. This means it is essential to either detect that the reference is still live and refuse to service any other borrows, or invalidate the reference in order to service other borrows. There is a Rust type that effectively performs this latter approach: RefCell<T>.

Raw pointers in Rust do not have such aliasing restrictions with regard to each other, so we are free to have any number of constant *const T and mutable *mut T pointers coexisting. Raw pointer semantics are just like they are in C, and are in fact even more lenient than C pointers since C pointers of differing types are not allowed to alias. You’re still not allowed to mess with ownership – the owner of the type still acts like your pointers don’t exist and so still assumes it is the arbiter of reads and writes – but if you have ownership of the type you can just make sure to only interact with it using raw pointers. This is exactly what UnsafeCell<T> and Cell<T> do to enable shared mutability, and those are the primitives fancy types like Rc<T> use to allow shared ownership.

Example of Borrowing and Aliasing

In this calculator code, Borrowing and Aliasing is demonstrated.

struct PostfixCalculator {
    stack: VecDeque<f64>,
}

impl PostfixCalculator {
    fn new() -> Self {
        PostfixCalculator {
            stack: VecDeque::new(),
        }
    }
}

Rust's borrowing rules ensure that references to data (borrowing) do not outlive the data they reference (ownership). This prevents dangling pointers

To learn more about borrowing and aliasing, it is recommended to read these official Rust resources: The Rust Programming Language chapter 4.2, and The Rustonomicon chapters 3.1 and 3.2.