#AskFedi: does anyone have a good article that explains *in depth* what Cell and RefCell are for in #Rust? So not just "this is what they do" or "this is how they work", but more questions like "why does this exist", "why would you need this", "what other approaches could have been taken and why was this one better".
(Do not send me ad-hoc explanations please; an explanation that fits into a toot is almost certainly not going to be in-depth enough here. I'm looking for articles that were written with deliberation and review.)
@dcreager That seems to have been the most useful one so far, thanks! It does seem to explain part of what I'm wondering about, but I'm having to make some inferences here as it mostly talks about Mutex/RwLock - do Cell and RefCell exist for the same reason of "doing runtime instead of compile-time checking"? And if so, and they are just not thread-safe, why would you ever use the not-thread-safe ones?
@joepie91 @dcreager there are situations where you don't need thread safety and still want multiple references to something potentially mutable. Keep in mind that Rust is strongly performance oriented.
Also, literally the most common use case for those types are actually Mutex and RwLock. They internally hold the value in UnsafeCell type to be able to work as intended. (But I recently wrote a Mutex implementation for single theaded async code, which works with RefCell and just yields when cell can't be borrowed)
I also often see it in a singleton.
> do Cell and RefCell exist for the same reason of "doing runtime instead of compile-time checking"?
That's why RefCell exists. Mutex/RwLock will _wait_ until it's your turn to get (exclusive) mut access. RefCell is single-threaded, so waiting doesn't make sense—there's no other thread that could make progress while you're waiting. For RefCell, taking >1 mut refs is a logic error, since the other mut ref has to be higher up your call stack, and you could have just passed that down!
> And if so, and they are just not thread-safe, why would you ever use the not-thread-safe ones?
Mutex/RwLock have to synchronize multiple threads, which requires atomic operations that aren't free. If you know you're only working in one thread, RefCell will be faster since it doesn't have to use atomic ops to query/update the "who's using this" state.
> do Cell and RefCell exist for the same reason of "doing runtime instead of compile-time checking"?
Cell is different — Cell actually lets you get rid of the checks completely! RefCell is for "mutate something in place". Cell is for "pull something out into scratch space, update it there, and put it back". The "pull it out"/"put it back" parts don't have to worry about other threads, and always leave _something_ valid in the cell, so everything is kosher to the borrow checker.
@joepie91 @dcreager I believe the answer is speed. If you have a situation where multiple parts of your code need to be able to write to a value, but they're all on the same thread, you don't need to do the relatively expensive atomic locking operations that Mutex and RwLock do.
Ime they're relatively niche. I almost never use Cell or RefCell in my code.