Move Closures and Threads
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.
Your Task
Implement two functions.
Implement count_chars_in_thread
pub 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);Implement greet_each
pub 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!"]);Notes
- Count characters, not bytes, so
"héllo"is 5. - Order is part of the contract. Spawning all the threads first and then joining them in the same order is enough to guarantee it, and it also keeps the threads running concurrently.
Hints
Click here to reveal hints
w.chars().count()counts characters;w.len()counts bytes.- Only one thread can own
prefix, so give each closure its ownprefix.clone()before themove. - Collect into a
Vecof 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.
Run to execute your code and see test results here.