Borrowing is the act of letting another piece of code access your value without taking ownership, by handing out a reference. A shared reference &T permits read-only access, while a mutable reference &mut T permits exclusive write access. The cornerstone rule — sometimes called "aliasing XOR mutability" — is that at any program point, a value may be accessed either through any number of shared references or through exactly one mutable reference, but never both at once. This rule is fundamental to Rust's safety guarantees: by preventing both data races at compile time and the kind of unsynchronized aliasing that can break compiler optimizations, it underpins the correctness of transformations like LLVM's noalias attribute on &mut T.
The enforcement of these rules is the job of the borrow checker, which runs at compile time on every Rust program. It rejects code that would create a dangling reference — a reference pointing to memory that has been freed or to a value that has gone out of scope — long before the program runs, so safe Rust cannot produce use-after-frees or double-frees. The borrow checker's lifetime tracking has steadily improved: the NLL (Non-Lexical Lifetimes) refinement, stabilized in Rust 1.31 in December 2018, ends a reference's lifetime at its last use rather than at the closing brace of its enclosing block, allowing more natural code to compile. An even more precise analyzer called Polonius is under development, designed to compute loan lifetimes through dataflow analysis and accept programs that NLL cannot.
References must always point to valid data, and the compiler enforces this even in subtle cases. You cannot, for instance, return a reference to a local variable from a function, because the local is dropped at the closing brace and would leave the reference dangling — the compiler rejects such code at compile time. Inside a single function, you may create any number of immutable references to a value, but the moment a mutable reference appears, all immutable references must be gone, and only one mutable reference may exist. These rules apply to iteration as well: v.iter() yields &T by immutably borrowing the vector, v.iter_mut() yields &mut T by exclusively borrowing it, and v.into_iter() consumes the collection entirely, yielding owned T values.