notes

Log | Files | Refs | README

borrowing_rules.md (2811B)


      1 # Borrowing Rules
      2 
      3 Rust enforces two hard rules at compile time:
      4 
      5 1. You can have any number of immutable references (&T) at the same time
      6 
      7 2. You can have only one mutable reference (&mut T) at a time — and when a
      8    mutable reference is active, no immutable references may exist either
      9 
     10 ### String example:
     11 
     12 #### Question: Why does rust only allow one muatble reference at a time?
     13 
     14 A String in Rust is heap-allocated and stores a pointer, a length, and a
     15 capacity internally. If you hold an immutable reference r1 to a String and then
     16 mutate it via a second mutable reference r2 — say by pushing characters — the
     17 String may reallocate its internal buffer to a new heap address. At that point,
     18 r1 would be pointing to freed memory, which is a dangling pointer and causes
     19 [undefined behavior](/memory_safety/segfault.md). Rust prevents this entirely at
     20 compile time.
     21 
     22 ```rust
     23 let mut s = String::from("hello");
     24 
     25 let r1 = &s;      // ✅ immutable borrow
     26 let r2 = &s;      // ✅ another immutable borrow — fine!
     27 let r3 = &mut s;  // ❌ compile error: can't borrow mutably while r1/r2 exist
     28 ```
     29 
     30 ### Furthermore:
     31 
     32 The single mutable reference rule actually solves several classes of bugs
     33 simultaneously: ​
     34 
     35 - Dangling pointers — mutation causes reallocation, invalidating old references
     36   (your example)
     37 
     38 - Data races — two threads mutating the same memory simultaneously leads to
     39   unpredictable results
     40 
     41 - [Iterator invalidation](iterator_invalidation.md) — modifying a collection
     42   while iterating over it (a common bug in C++ and Java)
     43 
     44 - Compiler optimisation safety — the compiler can safely optimise and even
     45   vectorise (SIMD) code because it knows no two mutable aliases can overlap
     46 
     47 ## Mutable Reference [Lifetime](/memory_safety/lifetime.md) Is Scoped
     48 
     49 The borrow checker is smart enough to track when a reference's last use is, not
     50 just its scope. This means a mutable reference can be created once the immutable
     51 ones are no longer actively used: ​
     52 
     53 ```rust
     54 let mut s = String::from("hello");
     55 
     56 let r1 = &s;
     57 let r2 = &s;
     58 println!("{r1}, {r2}"); // r1 and r2 last used here — they're effectively dropped
     59 
     60 let r3 = &mut s; // ✅ now safe, no active immutable refs
     61 r3.push_str(", world");
     62 ```
     63 
     64 This feature is called
     65 [Non-Lexical Lifetimes (NLL)](/memory_safety/non_lexical_lifetimes.md) and was
     66 introduced to make Rust's borrow checker less restrictive while keeping it safe.
     67 
     68 ## References:
     69 
     70 [Why Does Rust Enforce the “One Mutable or Many Immutable References” Rule in Single-Threaded Programs?](https://users.rust-lang.org/t/why-does-rust-enforce-the-one-mutable-or-many-immutable-references-rule-in-single-threaded-programs/121017/2)
     71 
     72 [The Problem With Single-threaded Shared Mutability](https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/)