We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 7 of 24
Sync: T is Sync exactly when &T is Send
Sum values by splitting it across workers threads that all accumulate into
one shared total.
pub fn shared_accumulate(values: Vec<i64>, workers: usize) -> i64
Empty input returns 0; treat workers == 0 as 1. The tests check
workers = 1, 4, 16 against the same expected number.
Yes, you could sum the chunks separately and add them up at the end — and in
real code you should. The point here is the shared cell, because it is where
Sync becomes concrete.
The starter’s error
It shares a RefCell<i64> between scoped threads. RefCell is the interior
mutability type you already know: it lets you mutate through a &, checking
the borrow rules at runtime instead of compile time. Perfect fit, one would
think.
error[E0277]: `RefCell<i64>` cannot be shared between threads safely
|
| s.spawn(move || {
| ----- ^------- `RefCell<i64>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `RefCell<i64>`
= note: required because it appears within the type `&RefCell<i64>`
= note: required for `&RefCell<i64>` to be `Send`
Read the last two note: lines in order. They are the definition:
T: Syncif and only if&T: Send.
Sharing a &T with another thread is the same act as sending a &T to
another thread. Sync is not an independent idea — it is Send applied to
references. That is why the compiler’s explanation walks from “not Sync“ to
“so &RefCell is not Send“ to “so the closure is not Send“.
And the reason RefCell is not Sync is the same reason Rc is not Send:
its borrow flag is a plain non-atomic counter. Two threads calling
borrow_mut() at once could both see “not borrowed” and both hand out a
&mut. Aliasing mutable references is instant undefined behaviour, so the
type opts out.
Non-Sync types are exactly the types with unsynchronised interior
mutability. Cell, RefCell, and the Rc counter. Everything else with no
interior mutability is Sync for free, because &T with no way to mutate is
harmless to share.
The fix: Mutex<i64>
Mutex<T> is interior mutability with synchronisation, so it is Sync.
Swap the type, change borrow_mut() to lock(), and it compiles.
Two details worth noticing while you do it:
-
lock()returns aResult, because the mutex might be poisoned — a thread panicked while holding it. There is a whole problem on that later in this track;.expect(...)is fine for now, and now you know it is not noise. -
into_inner()on the mutex at the end gives you the value without locking. It takesselfby value, which proves nobody else can be holding a reference, so there is nothing to synchronise against.get_mut()does the same trick with&mut self.
An AtomicI64 with fetch_add is also a correct answer here and is what you
would actually reach for. Either passes.
The asymmetry worth remembering
These two bounds in the standard library are not arbitrary, and comparing them
is the fastest way to see that Send/Sync bounds are derived from what
the API lets you do:
impl<T: ?Sized + Send> Sync for Mutex<T>
impl<T: ?Sized + Send + Sync> Sync for RwLock<T>
Mutex<T> is Sync when T is merely Send. Only one thread can hold the
guard at a time, so T is only ever transferred between threads, never
shared — and transfer is exactly what Send licenses.
RwLock<T> needs T: Send + Sync, because several readers hold &T
simultaneously. That is sharing, which is what Sync licenses. The extra
bound falls straight out of the extra capability. Nobody chose it.
Asking the compiler directly
There is a standard trick for asserting a marker-trait fact:
fn assert_sync<T: Sync>() {}
assert_sync::<Mutex<i64>>(); // compiles
assert_sync::<RefCell<i64>>(); // E0277 — the program no longer builds
Note the honest limitation: the positive assertion is a compile-time check
the grader here can enforce, but the negative one has no passing form —
a program containing it does not build, so it cannot pass a test. Marker-trait
membership is erased before codegen and there is no runtime is_sync::<T>().
Add the helper to your submission if you like; nothing in the tests can reward
it, and knowing why is worth more than the mark would be.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.