thread::spawn accepts any closure that is Send + 'static. The 'static part is the strict one: the spawned thread might still be running long after the function that created it has returned, so the closure is not allowed to borrow anything from that function's stack.
The move keyword is how you satisfy that. It changes capture mode: instead of borrowing the variables it mentions, the closure takes ownership of them. The data now lives inside the closure, which lives inside the thread, so there is no dangling reference to worry about.
Ownership being ownership, only one thread can have it. If two threads each need the same String, one of them needs its own copy. That is not a workaround, it is the rule being consistent: move really does hand the value over.
Implement two functions.
count_chars_in_threadpub fn count_chars_in_thread(
words: Vec<String>,
) -> usizeMove words into a spawned thread, count the characters across all of them, join, and return the total.
let words = vec![
"ab".to_string(),
"cde".to_string(),
];
assert_eq!(count_chars_in_thread(words), 5);greet_eachpub fn greet_each(
prefix: String,
names: Vec<String>,
) -> Vec<String>Spawn one thread per name. Each thread builds "{prefix}, {name}!". Return the greetings in the same order as names, whatever order the threads happen to finish in.
let names = vec![
"Ada".to_string(),
"Bo".to_string(),
];
let out = greet_each("Hi".to_string(), names);
assert_eq!(out, vec!["Hi, Ada!", "Hi, Bo!"]);"héllo" is 5.w.chars().count() counts characters; w.len() counts bytes.prefix, so give each closure its own prefix.clone() before the move.Vec of handles first. If you join inside the same loop that spawns, each thread finishes before the next one starts and you have written a slow sequential loop.handles.into_iter().map(|h| h.join().unwrap()).collect() turns the handles back into results, in order.thread::spawn accepts any closure that is Send + 'static. The 'static part is the strict one: the spawned thread might still be running long after the function that created it has returned, so the closure is not allowed to borrow anything from that function's stack.
The move keyword is how you satisfy that. It changes capture mode: instead of borrowing the variables it mentions, the closure takes ownership of them. The data now lives inside the closure, which lives inside the thread, so there is no dangling reference to worry about.
Ownership being ownership, only one thread can have it. If two threads each need the same String, one of them needs its own copy. That is not a workaround, it is the rule being consistent: move really does hand the value over.
Implement two functions.
count_chars_in_threadpub fn count_chars_in_thread(
words: Vec<String>,
) -> usizeMove words into a spawned thread, count the characters across all of them, join, and return the total.
let words = vec![
"ab".to_string(),
"cde".to_string(),
];
assert_eq!(count_chars_in_thread(words), 5);greet_eachpub fn greet_each(
prefix: String,
names: Vec<String>,
) -> Vec<String>Spawn one thread per name. Each thread builds "{prefix}, {name}!". Return the greetings in the same order as names, whatever order the threads happen to finish in.
let names = vec![
"Ada".to_string(),
"Bo".to_string(),
];
let out = greet_each("Hi".to_string(), names);
assert_eq!(out, vec!["Hi, Ada!", "Hi, Bo!"]);"héllo" is 5.w.chars().count() counts characters; w.len() counts bytes.prefix, so give each closure its own prefix.clone() before the move.Vec of handles first. If you join inside the same loop that spawns, each thread finishes before the next one starts and you have written a slow sequential loop.handles.into_iter().map(|h| h.join().unwrap()).collect() turns the handles back into results, in order.