Programming Fundamentals › Memory & Runtime
Ownership and Borrowing
Rust's compile-time rules that prevent memory bugs without a GC.
Also known as: ownership and borrowing, rust ownership, borrow checker
Ownership and borrowing are Rust’s rules for memory safety without a garbage collector and without manual free. They’re checked by the compiler, so programs that would have a dangling pointer or a use-after-free are rejected before they ever run.
The rules are small:
- Every value has a single owner.
- There is only one owner at a time; assigning or passing the value moves ownership.
- When the owner goes out of scope, the value is freed — automatically, deterministically.
- You can borrow the value with a reference instead of moving it, but: you may have either many shared (immutable) borrows or one mutable borrow, never both at once.
let s = String::from("hi"); // s owns the string
let r = &s; // r borrows it (shared)
println!("{r}"); // fine
// let m = &mut s; // would be rejected while r is in use
That “shared XOR mutable” rule is what makes Rust’s references safe without locks: the compiler guarantees no data race on borrowed data, and no reference outlives the value it points to (the “lifetimes” it checks).
The classic mistakes:
- Thinking it’s a garbage collector. It isn’t. Memory is freed exactly when the owner leaves scope — no background collector, no pause. That deterministic release is the selling point.
- Fighting the borrow checker with
.clone(). Cloning silences the error by copying data, but doing it everywhere gives you an expensive, unidiomatic program. Usually a small restructuring — a narrower borrow scope, or passing a reference — is the real answer. - Holding a borrow too long. Keeping a mutable borrow alive while you also need to read is the most common beginner error; the fix is to shorten the borrow’s scope.
- Expecting it for every shared value. Single ownership can’t express shared, cyclic data. That’s what
Rc/Arcand reference counting and weak references are for — you opt into runtime checks only where you need them.
Ownership is a third answer to “who frees the memory?”, alongside manual management and garbage collection: the compiler proves it, so there’s no runtime cost for the common case. It has a learning curve, but the classes of bugs it eliminates — use-after-free, double free, data races — are exactly the ones that plague systems code.