So I was thinking a bit about
RefCell. I often find that people are not sure of the best way to use them – particularly
RefCell – and the problem is that if you use them wrong, you are likely to get panics and frustration.
Background: my philosophy on cell and ref-cell
At its most simple, my advice is usually as follows: use
Cell when the data is copy; otherwise, only hold the
RefCell lock long enough to store some data in or to clone the data out and return it. If the data is too expensive to clone regularly, use an
Arc so it becomes cheap. Further, you should package up all access to those fields in simple accessors so you can easily audit what is being done.
More generally, the idea is to avoid holding the lock and then doing complex things. So e.g. returning a
Ref or invoking a closure while holding the lock is to be done with care, because you can’t easily audit what will be done while the lock is held. Likewise, invoking other methods on
self is risky, since those methods may evolve to be quite complex and may start trying to use other fields.
Note that this effectively models what you can do in most GC-languages, like Java or Ocaml, where you can’t take the address of a field, just load from it or store to it. This is no accident, I think, since aliased-mutability is so omnipresent in those languages. (IOW, every field in a Java class is effectively a
Gc<T> is some
Copy wrapper type for Gc-managed memory.)
A “magic cell” that only offers get/set
This leads me to an interesting thought. I think that the
Cell type, with its
set APIs, represents a better interface than
RefCell. It avoids the danger of holding the lock too long and I think is just generally more intuitive. Unfortunately, it is only applicable to
What if we offered a single type, let’s call it
MagicCell, that supports the
Cell API (i.e.,
set) but works for all
Clone data. I was thinking it would use specialization so that, internally, it uses a
Cell if the data is
Copy and a
RefCell otherwise. We can then promote this type as the Preferred Way to handle aliased-mutability (that is, market
RefCell as a kind of specialty tool for more advanced scenarios).
What we could even do, which would probably be better, is to just make cell itself be this “magic” type. IOW, all existing uses of
Cell continue to work (and have no space overhead), but you can now also use it for
Clone data (at the cost of a flag).
(We might then want to have a variant,
ReflessCell, that guarantees no flag, but works as
Cell today, except that it should also support a
swap method. This could used if you really want to optimize.)