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