title: Borrowing

Borrowing lets you use a value without taking ownership of it. You borrow a value by creating a reference with &.

Immutable references

let s = String::from("hello");
let r = &s;  // borrow s
 
println!("{r}");  // works
println!("{s}");  // also works — s is still the owner

You can have as many immutable references as you like at the same time.

Mutable references

Add mut to borrow mutably:

let mut s = String::from("hello");
let r = &mut s;
 
r.push_str(", world");
println!("{r}");  // "hello, world"

The rules

The borrow checker enforces two rules at compile time:

  1. You can have any number of immutable references, OR exactly one mutable reference — never both at the same time
  2. References must always be valid (no dangling references)
let mut s = String::from("hello");
 
let r1 = &s;
let r2 = &s;      // fine — multiple immutable refs
let r3 = &mut s;  // ERROR — can't have mutable ref while immutable refs exist
 
println!("{r1}, {r2}, {r3}");

Why these rules?

The rules prevent data races at compile time:

  • Multiple readers are safe — none can change the data
  • A single writer is safe — nothing else is reading
  • Both at once is undefined behaviour in other languages — Rust simply forbids it

Non-lexical lifetimes

References only need to follow the rules while they’re actually in use. Once a reference is last used, it’s considered dropped even if it’s still technically in scope:

let mut s = String::from("hello");
 
let r1 = &s;
let r2 = &s;
println!("{r1}, {r2}");  // r1 and r2 last used here — effectively dropped
 
let r3 = &mut s;  // fine now
println!("{r3}");

Dangling references

Rust prevents you from returning a reference to a value that no longer exists:

fn dangle() -> &String {  // ERROR
    let s = String::from("hello");
    &s  // s is dropped at end of function — this reference would be invalid
}

The fix is to return the owned value instead:

fn no_dangle() -> String {
    let s = String::from("hello");
    s  // ownership is moved out, no problem
}