We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 10 of 24
Arc gives you sharing, not mutability
Every worker computes the sum of its chunk and pushes it into one shared
Vec. Return that vector, sorted ascending.
pub fn collect_into_shared(values: Vec<i64>, workers: usize) -> Vec<i64>
Chunk with values.chunks(values.len().div_ceil(workers)). Empty input gives
[]; treat workers == 0 as 1.
“I wrapped it in an Arc, why can’t I mutate it?”
This is the most-asked question in Rust concurrency, and the starter asks it for you:
error[E0596]: cannot borrow data in an `Arc` as mutable
|
| sink.push(sum);
| ^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference,
but it is not implemented for `Arc<Vec<i64>>`
The answer is in the second half of that message. Arc<T> implements Deref
but deliberately not DerefMut. All it can ever give you is a &T.
And it must be that way. Arc exists so that several handles point at one
value. If it handed out &mut T, two threads could hold &mut to the same
data simultaneously, and aliasing-xor-mutation — the rule underneath the
entire borrow checker — would be broken by a library type. Arc cannot offer
mutation without giving up the thing it exists for.
Two orthogonal questions
The framing that fixes this permanently:
-
Arcanswers “who owns this?” — several handles, freed when the last one drops. It is about lifetime. -
Mutexanswers “who may touch it right now?” — one thread at a time, enforced. It is about access.
They are independent concerns, so you compose them. And once you see it that way, the notorious type stops being a magic incantation:
Arc<Mutex<Vec<i64>>>
Read outside-in: shared ownership of a lock protecting a vector. Every layer is doing one job.
Why never Mutex<Arc<T>>
Learners write this by accident, and it type-checks, which is why it is
worth naming. Mutex<Arc<Vec<i64>>> is a lock protecting a handle. You can
lock it and swap which allocation the handle points to — but the Vec inside
is still reached through an Arc, so it is still immutable, and the mutex is
guarding the wrong thing. It also cannot be shared, because the Mutex is the
outermost type and there is only one of it.
If you find yourself writing Mutex<Arc<...>>, you almost certainly wanted
the letters the other way round.
Why the result is sorted, and why that is not the real answer
The order in which workers reach push is the scheduler’s business, not
yours. Run the same input twice and you can get [26, 10, 19] then
[10, 26, 19]. Sorting collapses every possible interleaving onto one value,
which makes the function testable.
Do not conclude that sorting is the technique. It works here only because
the answer happens to be a set of numbers. When order carries meaning — a
parallel map where output i must correspond to input i — sorting
destroys the information you needed. The general answer is index tagging:
send (index, result) and reassemble by index. That gets a problem of its own
later in this track, and it is the one to remember.
Recovering the data at the end
You have an Arc<Mutex<Vec<i64>>> and you need to return a Vec<i64>. Three
reasonable ways, all fine here:
-
lock it and
clone()the contents; -
lock it and
std::mem::take(&mut *guard), leaving an emptyVecbehind and avoiding the copy; -
Arc::into_inner(shared)(which returnsSomeonly when yours is the last handle — true, because every worker has been joined) followed byMutex::into_inner.
The last one is the most interesting: both into_inners take self by value,
and owning the container is already proof that nobody else can be looking, so
neither needs to lock or synchronise anything.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.