3.2K WORDS · KOHTA KOUCHI
Understanding Rust's ownership and borrowing
The one feature that sets Rust apart — memory safety with no garbage collector, enforced at compile time.
What sets Rust apart from most languages is its ownership system: it guarantees memory safety at compile time, with no garbage collector running underneath. The whole model rests on three rules. First, every value has exactly one owner — assign a String to another variable and ownership moves to it, leaving the original invalid. These move semantics are what stop two parts of your program from owning the same memory.
The second rule: when an owner goes out of scope, its value is dropped automatically. Leave a function or a block and the memory is freed for you — no manual free(), no leaks. The third: passing a value into a function moves ownership into that function, so it's always unambiguous who is responsible for a value.
Ownership alone would make sharing painful, so Rust adds borrowing. An immutable borrow (&) lends a value out read-only — the borrower can read but not mutate — which lets many readers hold a reference at once while the owner keeps ownership. A mutable borrow (&mut) is for when the borrower genuinely needs to change the value, like pushing onto a String.
The rule that ties it together: you can't mix immutable and mutable borrows. At any moment you may have any number of immutable borrows, or exactly one mutable borrow — never both. That single constraint is what makes data races impossible. Since Rust 2018, Non-Lexical Lifetimes make this ergonomic: a borrow lasts only until its last use, not until the end of the lexical scope, so you can start a new borrow sooner.
When the compiler can't infer how long a reference stays valid, you annotate lifetimes explicitly (the 'a syntax) — a function that takes two references and returns one has to say which input the result lives as long as. The compiler also kills dangling references outright: try to return a reference to a value that's about to leave scope and it won't compile — move ownership out instead. And reach for borrowing over clone(): cloning copies, which costs; borrowing just hands over a reference.
Internalise these rules and you get memory safety, GC-free performance, thread safety and explicit control all at once. They feel strict at first, but the compiler is really just walking you through decisions you'd have had to make anyway — and catching the bugs before they ship.