Cell::get_mut(... require &mut self. Why?

Cell::set, Cell::replace, Cell::as_ptr -> *mut T

all only requires &self

Why is it that Cell::get_mut require a &mut self ? If I can modify it via set/replace with just a &self, why does get_mut require &mut self.

I'm running into this particular problem because I'm using Rc<Cell<T>>

Also, RefCell::borrow_mut also only requires &self.

In Rust, you can never have another reference to the value while the &mut of it is alive. It's one of the core language guarantees that makes Rust unique. There's no exception regards this rule in safe Rust. If you made an exception via some unsafe code, it's bug no matter why and how.

But sometimes we need a shared mutability. One way to implement it is to ensure the rule by some runtime check. RefCell and Mutex are examples. You can take &mut of the inner value from the & of the wrapper type, and the runtime check prevents to hold two &mut at the same time.

But there's another approach here - simply not allow to borrow the inner value. You can see that all the methods of Cell(except the .get_mut() here) takes/returns things by-value, not by-reference. Brilliant, isn't it? It's not possible to holds two &mut T from the same Cell<T> - because you can't have even one!

But all this stories are about to handle shared mutability. If you don't share(for now) the Cell<T>, nothing bad can happen when you obtain the reference of the inner value. That's why all those shared mutability types in std(Cell, RefCell, Mutex, Atomic* etc) have fn get_mut(&mut self) -> &mut T method.

5 Likes

This (different approach, via by value) is the key insight I missed. Thanks for explaining!

1 Like

And also Cell : !Sync, meaning that since it mutates through a (potentially) shared reference without any synchronization primitive, it could lead to data races if used across multiple threads. By making it !Sync, safe code is then not able to share a Cell (i.e., Send Copyies of a shared reference & _ to a Cell) across multiple threads.

You can, however, get multithread-safe by-value mutation through atomics.

You can read this blog post of mine for more info about the difference between a mutable reference and a &mut reference: https://danielhenrymantilla.github.io/posts/2019-02-24-mutation-part-2-to-mut-or-not-to-mut/