How does Rust manage memory without a garbage collector?
Short answer: Every value in Rust has one owner, and the free happens at a point you can
name — the closing brace of the scope that owns it. The compiler writes the free in at compile
time; nothing runs in the background to reclaim memory. The cost shows up when several parts
of a program need one value: Rc shares it, and the memory then lives as long as the last
Rc does.
The free has a time and a place
Section titled “The free has a time and a place”What is ownership in Rust? states the rule: one owner
per value, and the owner’s scope decides when the value dies. “Goes out of scope” is not a
figure of speech. The destructor runs at the closing brace, before the next statement, in
reverse order of declaration, inner scopes before outer ones. A file handle closes there. A
MutexGuard releases there. Nothing decides later that the file is unreachable — the
handle’s own destructor closes it, at the only point where the compiler can prove nothing
else is using it.
flowchart LR A["value created in a scope"] --> B["the scope ends"] B --> C["drop runs, memory freed"] D["a garbage collector"] --> E["runs when it decides"] E --> F["memory freed sometime later"]
That is what deterministic buys: no pause while a collector walks the heap, and no background
thread keeping counts or mark bits up to date. The release point is written into the braces,
so you can read it off the page. If the end of the scope is later than you want,
std::mem::drop ends it early.
What a collector does instead
Section titled “What a collector does instead”A garbage collector has to work out what is still reachable, and it does that work while the program runs — pausing to trace the heap, or touching a count on every assignment. Ownership moves the same work to compile time: with one owner per value, the free has exactly one place it can go. Why does Rust have ownership? covers the two bugs both approaches rule out.
The cost of Rc
Section titled “The cost of Rc”One owner per value can’t say “two functions both hold this”. Rc<T> can: it keeps a
reference count, and Rc::clone bumps the count instead of copying the data. The value is
dropped when the count reaches zero — when the last Rc dies, not when the scope that
created it ends. Every clone and every drop touches that count, which is runtime work a
Box never does.
Two effects follow. A value stashed inside a longer-lived structure stays alive for exactly
as long as any handle points at it, which is the point — but it also means memory can be
held well past the scope that made it. And a cycle leaks: two Rcs pointing at each other
keep each other’s count above zero, so neither is ever dropped. Rc is single-threaded as
well; across threads the atomic-counted Arc is the one to reach for. Both belong to the
smart pointers chapter, and so does Weak, the handle that breaks a
cycle.
Related
Section titled “Related”- What is ownership in Rust? — the rule this page follows to its conclusion
- Why does Rust have ownership? — the bugs a collector and ownership each prevent
- When should you clone? — when shared ownership beats copying
- Smart pointers and sharing —
Rc,Arc, andWeak - Moves and ownership — the rest of this chapter
- Next in this chapter: how to understand ownership if you come from Python or JavaScript