GoByte Skills Episode 11 of 27, track Rust (2 of 3)

Ownership: one owner, no garbage collector

GoByte Skills #11: Rust frees memory without a garbage collector because every value has exactly one owner. Move it and the old name is dead. When the owner's scope ends, the compiler has already written the drop call.

Piere is one of GoByte's characters. This post was drafted by AI agents in Piere's voice, then fact checked, run and edited by the GoByte team.

Back to top

Each value has one owner and is freed when the owner goes out of scope, which is how Rust frees memory without a collector. Nothing scans the heap at runtime: the compiler decides, while compiling, the line where each value dies.

Animation for GoByte Skills #11: A value moves between owners and is dropped at the end of the last scope.
Transcript

Rust code types in while a value box moves from owner a to owner c, a second value moves into take and is dropped when that function ends, and the first is dropped at the closing brace of main.

struct Noisy(&'static str);

impl Drop for Noisy {
    fn drop(&mut self) {
        println!("drop {}", self.0);
    }
}

fn take(n: Noisy) {
    println!("take owns {}", n.0);
} // n's scope ends here

fn main() {
    let a = Noisy("a");
    let b = Noisy("b");
    let c = a; // move: `a` is now unusable
    take(b); // move into take
    println!("main ends, c holds {}", c.0);
} // c's scope ends here

Output:

take owns b
drop b
main ends, c holds a
drop a

The mechanism#

let c = a is a move: the value's bytes are copied into c and the name a is dead. For a String those bytes are a pointer, a length and a capacity; the heap buffer stays put (same address, checked with as_ptr). Use a after the move and the build fails with E0382, "borrow of moved value: a". One live owner means exactly one place to free, so the compiler inserts the drop call at the end of that owner's scope. take(b) moved b into the function, which is why b died before main printed its last line.

Conditional moves#

In if flag { take(x) } the compiler cannot know statically whether x is still owned, so it keeps a hidden drop flag on the stack and checks it at the closing brace. Still no collector: one boolean.

Edges#

Copy types (integers, bool, shared references) are copied, not moved, so the original stays usable. Locals drop in reverse order of declaration. And dropping is not a promise: std::mem::forget is a safe function, and an Rc cycle never reaches a count of zero. Rust guarantees memory safety, not the absence of leaks.

Determinism#

The same rule closes files, releases locks and returns pooled connections at a line you can point to, not whenever a collector gets around to it. It is RAII, plus a compiler that refuses to let you touch the object after it is gone.

Rule of thumb#

For every value, you should be able to say who frees it and on which line. Rust does have a garbage collector. It runs at compile time and its name is the closing brace.

Report a mistake

Your product here? Partner with us

Back to top

Discussion

No comments yet. Signed in GoByte members with a verified e-mail can join. Community guidelines

Reading is open to everyone. Commenting and voting need a GoByte account with a verified e-mail.