Borrowing & References
TypeScript passes references freely — Rust tracks them
Section titled “TypeScript passes references freely — Rust tracks them”In TypeScript, passing an object to a function gives the function a reference. Both the caller and the callee can read and write the object simultaneously. The language imposes no restriction on how many references exist or what they can do.
Rust’s borrow checker enforces strict rules about references at compile time. The rules prevent data races, dangling pointers, and use-after-free — all at zero runtime cost.
Shared references — &T
Section titled “Shared references — &T”A shared reference (&T) lets you read a value without taking ownership. Many shared references can exist at the same time.
// TypeScript — just passing a reference (no rules enforced)function getLength(s: string): number { return s.length;}
const greeting = "hello";const len = getLength(greeting);console.log(greeting, len); // both still validfn get_length(s: &String) -> usize { s.len() // we borrow s — we cannot move out of it}
fn main() { let greeting = String::from("hello"); let len = get_length(&greeting); // lend greeting to the function println!("{} has {} chars", greeting, len); // greeting is still valid here — we only borrowed it}Mutable references — &mut T
Section titled “Mutable references — &mut T”A mutable reference (&mut T) lets you both read and modify a value. But Rust enforces an exclusive rule: while a &mut reference exists, no other reference — shared or mutable — may exist at the same time.
// TypeScript — no restriction on simultaneous read + writefunction appendWorld(s: { value: string }): void { s.value += ", world";}
const msg = { value: "hello" };appendWorld(msg);console.log(msg.value); // "hello, world"fn append_world(s: &mut String) { s.push_str(", world");}
fn main() { let mut s = String::from("hello"); append_world(&mut s); println!("{}", s); // "hello, world"
// Multiple shared refs are fine — no mutation happening let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2);}The classic borrow checker error
Section titled “The classic borrow checker error”This is the most common error new Rust developers encounter. The code below tries to hold a shared reference and a mutable reference at the same time:
fn main() { let mut s = String::from("hello");
let r1 = &s; // shared borrow begins let r2 = &s; // another shared borrow — still fine let r3 = &mut s; // ERROR: cannot borrow `s` as mutable because it is also borrowed as immutable
println!("{}, {}, {}", r1, r2, r3);}// error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable// --> src/main.rs:6:14// |// 4 | let r1 = &s;// | -- immutable borrow occurs here// 6 | let r3 = &mut s;// | ^^^^^^ mutable borrow occurs here// 7 | println!("{}, {}, {}", r1, r2, r3);// | -- immutable borrow later used hereThe fix is simple: make sure shared and mutable borrows do not overlap. End the shared borrows first (they end when they are last used), then take the mutable borrow:
Try it
Section titled “Try it”fn append_world(s: &mut String) { s.push_str(", world");}
fn main() { let mut s = String::from("hello"); append_world(&mut s); println!("{}", s);
// multiple shared refs are fine let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2);}Compiling…