move Closures
move closures capture variables by value into the closure environment, required for thread::spawn and long-lived callbacks.
Search across all documentation pages
move closures capture variables by value into the closure environment, required for thread::spawn and long-lived callbacks.
use std::thread;
fn main() {
let data = vec![1, 2, 3];
thread::spawn(move || println!("{:?}", data));
}When to reach for this: Threads, tokio::spawn, timers, and when closure may outlive stack borrows.
fn spawn_sum(nums: Vec<i32>) -> std::thread::JoinHandle<i32> {
std::thread::spawn(move || nums.iter().sum())
}
fn main() {
let h = spawn_sum(vec![1, 2, 3]);
println!("{}", h.join().unwrap());
}What this demonstrates:
nums moved into closurenums after spawnJoinHandle returns result from threadmove applies to all captured variables, not selective. Use clone before move if both thread and main need data.
move for short-lived closures.Copy types - Still "move" semantics but bits copied.Arc clone before move.Send. Fix: Mutex.| Alternative | Use When | Don't Use When |
|---|---|---|
Arc + move | Shared read across threads | Single owner |
| Scoped threads | Borrow stack data | Detached thread |
| Channel send | Hand off ownership | Shared state needed |
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 16, 2026