A thread can be created by spawning it; this creates an independent detached child thread that in general can outlive its parent. This is demonstrated in the following code snippet:
// code from Chapter 9/code/thread_spawn.rs: use std::thread; use std::time; fn main() { thread::spawn(move || { println!("Hello from the goblin in the spawned thread!"); }); }
The argument to spawn is a closure (here, without parameters, it is indicated as ||), which is scheduled to execute independently from the parent thread, which is shown here as main(). Notice that it is a moving closure which takes ownership of the variables in a given context. Our closure here is a simple print statement, but in a real situation this could be replaced by a heavy or time-consuming operation.
When we execute this code, we normally don't see any output. Why is this? It turns out that main() is a bad parent (as far as threading is concerned) and doesn't wait for its children to end properly. The output of the spawned thread only becomes visible if we let main() pause for a brief moment before terminating. This can be done with the thread::sleep method,which takes the Duration, a type defined in the std::time module:
fn main() { thread::spawn(move || { ... }); thread::sleep(time::Duration::from_millis(50)); }
This prints out the following:
Hello from the goblin in the spawned thread!.
In general, this pause period is not needed. Child threads that are spawned can live longer than their parent threads, and will continue to execute when their parents have already stopped.
Better practice in this case, however, is to capture the handle join that spawn returns in a variable. Calling the join() method on handle will block the parent thread, making it wait until the child thread is finished. This returns a Result instance. unwrap() will take the value from Ok, returning the result of the child thread, which is () in this case because it is a print statement, or panic in the Err case:
fn main() { let handle = thread::spawn(move || { println!("Hello from the goblin in the spawned thread!"); }); // do other work in the meantime let output = handle.join().unwrap(); // wait for child thread println!("{:?}", output); // () }
If no other work has to be done while the child thread is executing, we can also write this:
thread::spawn(move || { // work done in child thread }).join();
In this case, we are waiting synchronously for the child thread to finish, so there is no good reason to start a new thread.
Why do we need a moving closure when spawning a thread? Examine the following code, which does not compile:
// code from Chapter 9/code/moving_closure.rs: use std::thread; fn main() { read(); } fn read() { let book = read_book("book1.txt"); thread::spawn(|| { println!("{:?}", book); }); } fn read_book(s: &str) { }
This gives us error: closure may outlive the current function, but it borrows `book`, which is owned by the current function. Indeed, the closure may be run by the spawned thread long after the read() function has terminated, as it has freed the memory of book. But if we make it a moving closure, which takes ownership of its environment, the code compiles:
thread::spawn(move|| { println!("{:?}", book); });