Understanding Ownership
The mechanism behind Rust's memory safety is called the Ownership system, enforced by a compiler component called the Borrow Checker. Every value in Rust is governed by three rules:
- Each value has exactly one owner — a variable that is responsible for that value.
- There can only be one owner at a time. You can pass ownership to another variable, but the original variable is then invalid.
- When the owner goes out of scope, the value is automatically dropped — its memory is freed immediately and deterministically, with no GC needed.
This is like a library that has only one physical copy of a book. The library (the program) owns the book. When you check it out, you become the owner. When you return it (go out of scope), it's immediately available again. There's no waiting for a librarian to do a weekly audit of unreturned books.
The Rules of Borrowing
Transferring ownership every time you want to use data would be impractical. Rust allows you to temporarily borrow data — access it without taking ownership — through references. The Borrow Checker enforces two strict rules about references:
- Rule 1 — Many readers: You can have any number of immutable references (
&T) to data simultaneously. Many people can read the same document at once. No one can change it while others are reading. - Rule 2 — One writer: You can have exactly one mutable reference (
&mut T) to data at a time, and only when no immutable references exist. One person can edit the document, but only when everyone else has put their copy down.
These rules are not arbitrary. They mathematically prevent data races — the class of bugs where two concurrent operations access the same memory and at least one is writing, producing unpredictable results. In Go, you can write code with data races and it will compile and run — you need the -race flag and runtime luck to catch them. In Rust, data races are impossible by construction.
If you break these rules, the Rust compiler produces detailed, actionable error messages that explain exactly which borrow conflicts with which other borrow, and often suggests how to fix it. This is the steepest part of Rust's learning curve — "fighting the borrow checker" is a rite of passage — but it forces you to reason correctly about concurrent access patterns before your code ships.
A common interview question is: "How does Rust prevent data races?" The answer is: "Through the borrow checker's aliasing rules — at any point in time, a value can have either multiple immutable references OR exactly one mutable reference, never both. This is enforced at compile time, making data races a compile error rather than a runtime bug."