In this lesson, you will learn how to safely transfer ownership of data between threads so you can process information in parallel without risking data corruption.
The Challenge of Crossing Boundaries
When you spawn a thread in Rust, the new thread has its own scope. If you want to move data from your "main" thread into a new thread, the compiler needs to be absolutely certain that the data is valid for as long as that new thread exists.
If you try to use a local variable inside a new thread, the compiler will stop you. Why? Because the main thread might finish and drop that variable while the new thread is still trying to read it. This is a classic "use-after-free" danger that Rust eliminates.
Moving Ownership to Threads
To bridge this gap, we use the move keyword. By placing move before the thread closure, you instruct Rust to take ownership of the variables used inside the block and move them entirely into the new thread.
use std::thread;
fn main() {
let data = String::from("Hello from the main thread!");
// The 'move' keyword forces the thread to take ownership of 'data'
let handle = thread::spawn(move || {
println!("{}", data);
});
handle.join().unwrap();
}
In the example above, once the move happens, data is no longer accessible in the main thread. Ownership has been transferred. The new thread now "owns" that string, meaning it is responsible for cleaning it up when the thread finishes execution.
Why this is Safe
This approach follows the fundamental rule we’ve been building on: at any given time, there is only one owner. By moving the data into the thread, we ensure there is no possibility of a race condition where two threads try to modify or drop the same memory simultaneously. The compiler tracks this move, ensuring the main thread cannot touch the data once it has been handed off. If you need to share access without giving up ownership, we will look at synchronization tools later, but for now, remember that moving is the cleanest way to guarantee thread safety.
Key Takeaway: The move keyword transfers ownership of data into a new thread, ensuring the new thread has exclusive responsibility for that memory and preventing data races.