Vissza a blograBack to blog
Rust TypeScript

Borrowing in Rust: references and the borrow checker

Krisztián

Krisztián

2026. márc. 20.March 20, 2026

4 perc olvasásmin read

Last time we talked about Ownership. Rust’s way of making sure every value has exactly one owner. But if everything had to be moved every time you passed it somewhere, writing code would be a nightmare. That’s where borrowing comes in.

In Rust, you can lend a value to a function or variable without giving up ownership. You do this with references: &T for an immutable borrow, and &mut T for a mutable one.

The JavaScript comparison

In JavaScript, when you pass an object to a function, you’re handing over a reference automatically. You don’t think about it. You can mutate it, pass it to ten other places at the same time, and nothing stops you. Rust has references too, but with hard rules enforced at compile time:

That’s it. Two rules. But they eliminate an entire class of bugs: no data races, no “who mutated this?” debugging sessions.

Why does this feel weird at first?

Because in JS/TS, mutation is everywhere and implicit. You never declare intent. Rust forces you to be explicit: are you reading, or are you writing? And if you’re writing, you’re doing it alone. The compiler is essentially your strict code reviewer who never lets a shared mutable state slip through.

const in JS only protects the binding. The value itself is still wide open — you can’t stop someone from mutating the object you passed them. Rust’s &T goes deeper: it protects the value itself. If you only have an immutable reference, mutation is off the table, the compiler guarantees it.

Once it clicks, you stop seeing the borrow checker as an enemy and start seeing it as the teammate who catches the race condition before it ever reaches production.

Example

fn main() {
    let mut message = String::from("Hello");
    let r1 = &message;       // immutable borrow ✓
    let r2 = &message;       // another one — totally fine ✓

    println!("{} {}", r1, r2); // r1 and r2 are no longer used after this point

    let r3 = &mut message;   // mutable borrow ✓ (previous borrows are done)
    r3.push_str(", world!");

    println!("{}", r3);
}

Compare this to JS, where there are no rules at all:

let message = { text: "Hello" };

const r1 = message; // anyone can read
const r2 = message; // anyone can mutate
r2.text = "oops";   // r1 just got silently changed too 👀

In Rust, that silent mutation simply cannot happen. The compiler won’t allow a mutable reference to exist alongside any other reference.