We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 2 of 24
move closures: why the compiler forces your hand
Split data into pieces, sum each piece on its own thread, and return the
per-piece sums in order.
pub fn own_and_sum(data: Vec<i64>, parts: usize) -> Vec<i64>
The chunking convention is fixed, so read it carefully. Use
data.chunks(size) with size = data.len().div_ceil(parts). That is the same
rule the standard library uses: equal-sized pieces with a possibly-short tail.
It may produce fewer than parts pieces — for len = 5, parts = 4 the
size is 2, so you get three chunks [2, 2, 1], not four. Return one sum per
chunk, in chunk order.
Guard the degenerate inputs: parts == 0 and an empty data both return
[]. (chunks(0) panics, so you must handle them before you get there.)
The starter does not compile, and that is the lesson
It fails with E0373:
error[E0373]: closure may outlive the current function, but it borrows
`piece`, which is owned by the current function
|
| .map(|piece| thread::spawn(|| piece.iter().sum::<i64>()))
| ^^ ----- `piece` is borrowed here
| |
| may outlive borrowed value `piece`
help: to force the closure to take ownership of `piece`, use the `move` keyword
Read the first clause literally: may outlive. The compiler is not claiming
your thread will still be running when piece dies. It is saying it cannot
prove otherwise, and for thread::spawn there is nothing to prove — the
handle can be dropped, the thread detached, and the closure is then free to
run for the rest of the process’s life.
That is why thread::spawn requires F: Send + 'static. The 'static bound
is the formal way of saying “this closure must not borrow anything with a
shorter life than the program”. A closure that borrows a local is not
'static, so it is rejected — before any thread exists, at compile time,
every time. This is the whole “fearless” claim in one bound.
move fixes it by changing what the closure captures: instead of a
reference to piece, it captures piece itself. The Vec is moved into the
closure, the closure owns it outright, and its lifetime is now 'static
because it borrows nothing.
The second error, which is also part of the lesson
Adding move usually produces a new error, so expect it:
error[E0382]: borrow of moved value: `data`
If you move something into a thread and then read it afterwards in the
parent, the parent no longer owns it. There is exactly one value, and you gave
it away. This trips people because “it’s just a thread” feels like it should
not affect ownership — but a thread is not a special case, it is an ordinary
closure that happens to run elsewhere.
The fix here is structural rather than syntactic: do not read the moved data
again — return the results instead. Each worker owns its piece, computes
from it, and hands back a number through join(). Nothing needs to look at
data afterwards. When you find yourself fighting E0382 around a thread, the
question is almost always “what value do I actually need out of this?” rather
than “how do I get my variable back?”.
This is also why the signature takes Vec<i64> by value and the chunks
get .to_vec()‘d. Copying the data is the crude way to satisfy 'static.
Two problems from now thread::scope will let you delete both the copy and
the move, and you will appreciate it far more for having done it this way
first.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.