Rust frees memory at the end of a scope, not later

No garbage collector. Each value is freed by its owner, at a line you can point to, on the owner's thread.

Read the explanation

Predict first

b was moved into the worker's closure. Which thread frees it, and when?

012345678910main: create a, b, cnewuse tmpfree tmpmain: after the blockaftermain: spawn worker, move bspawnjoin()main endsfree cfree aprint bfree b

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 1/5

No garbage collector here. Each value is freed by whoever owns it, at a line you can see.

1struct Buffer(&'static str);2 3impl Buffer {4    fn name(&self) -> &str {5        self.06    }7}8 9impl Drop for Buffer {10    fn drop(&mut self) {11        println!("free {} on {}", self.0, thread::current().name().unwrap());12    }13}14 15fn main() {16    let _a = Buffer("a");17    let b = Buffer("b");18    let _c = Buffer("c");19    {20        let tmp = Buffer("tmp");21        println!("using {}", tmp.name());22    } // tmp is freed here, before the next line23    println!("after the block");24    let worker = thread::Builder::new().name("worker".into());25    let h = worker.spawn(move || println!("worker got {}", b.name())).unwrap(); // b moves to the worker26    h.join().unwrap();27    println!("main ends");28} // c, then a, are freed here: reverse order. b already was, by its new owner

Explanation

What just happened

Rust has no garbage collector. Each value has one owner, and it is freed when its owner goes out of scope. tmp was freed the moment its block ended, before after the block printed. c and a were freed when main ended, in reverse order.

b moved into the worker’s closure, so the worker owned it. The worker freed it, on its own thread, as soon as the closure ended.

Why it works like this

The compiler knows who owns each value, so it inserts the free at the end of the owner’s scope. Nothing tracks memory at run time, and nothing ever stops your threads to clean up.

Watch out

A move closure captures only the fields it uses. move || b.0 copies the field and leaves b where it was, so b is freed there, not on the new thread.

Common questions

When does Rust free memory?

When the value's owner goes out of scope. Drop::drop runs right there, before the next line, on the thread that owns the value.

In what order are values dropped?

Values in one scope are dropped in reverse order of declaration: last declared, first freed.

Which thread frees a value moved into a thread?

The new owner. A value moved into a spawned thread's closure is freed by that thread when the closure ends.

Verified: this timeline matches a real program, checked in CI on Rust 1.99 with Tokio 1.54. How we verify. Unclear or wrong? Tell us