Skip to content
Select theme

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.

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.

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.

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.