Theme
Q036. How do closures capture ownership, and what does move mean for a closure?
Short Answer
Closures capture only the environment places they need, by shared borrow, mutable borrow, or value, as inferred from use. A move closure forces capture by value, transferring non-Copy captures and copying Copy captures into the closure. move does not automatically make a closure FnOnce; the body determines whether captured values are moved out when called.
Deep Dive
A closure is an anonymous compiler-generated struct whose fields represent captures. Capture mode is chosen independently from call behavior:
- reading can capture by
&T; - mutation can capture by
&mut T; - consuming a value requires capture by value;
moverequests by-value capture even when the body only reads.
The closure traits depend on what invocation does with captures. A closure that only reads owned captures can implement Fn and run repeatedly. Mutating captures requires FnMut. Moving a captured non-Copy value out of the closure requires FnOnce.
By-value capture is useful when the closure must outlive the current stack frame, such as a spawned thread or many async tasks. Required Send and lifetime bounds are separate checks.
Internal Model
rustc creates a closure type containing captured places, often using disjoint field capture. A move closure stores owned values or copied Copy values in those fields. Dropping the closure drops owned captures, although the relative drop order of by-value captures is unspecified.
Closure calls lower through Fn, FnMut, or FnOnce receiver methods using &self, &mut self, or self respectively.
Example
rust
fn main() {
let message = String::from("hello");
let print = move || println!("{message}");
print();
print(); // allowed: the body only borrows its owned capture
// println!("{message}"); // error: message moved into the closure
}Common Mistakes
- Saying
movemeans every captured value is physically deep-copied. - Assuming every
moveclosure implements onlyFnOnce. - Confusing capture mode with
Send,Sync, or'staticrequirements.
Follow-ups
- What makes a closure implement
Fn,FnMut, orFnOnce? - How does disjoint field capture affect partial moves?
- Why do thread and async APIs frequently require
moveclosures?
Related
- Q011. What is a move in Rust?
- Q016. What is a partial move?
- Q035. How do collections and iterators transfer ownership?
References
- The Rust Programming Language
- Rust Reference
- Rust Edition Guide