4 ms·
How does the Cell type deal with possibilities like the one you described, of holding a string?
by hungrySometimes 6y ago
How does the Cell type deal with possibilities like the one you described, of holding a string?
- saagarjha 6y agoCell basically takes ownership of what you put inside of it. It lets you pull out a copy of the value so that when you mutate it later you’re still holding on to your copy even though the value stored inside of the Cell is dropped at that point as the Cell’s contents are replaced with the new value.
- comex 6y agoBy forbidding you from borrowing the interior of a Cell. For example, if you have an &Cell<String> (aliasing mutable reference to owned string), you can't go from that to &str (borrowed immutable string). So what can you do with a Cell? Well, you can always set() the value. As for getting the existing value: 1. If the interior is a Copy type like an integer, you can just call get(). Doesn't work for strings. 2. For any type, you can call replace(), which sets the value and returns the old one. You're then the exclusive owner of the old value since it's not in the Cell anymore. 3. With the `safe_cell_exts` crate, you could also call get_clone() to clone the interior. For a Cell<String> this would allocate a new string and copy the contents, which is slow. More usefully, for a Cell<Rc<String>> it would simply increment the reference count. Why is get_clone()'s functionality sitting in some crate rather than the standard library? Well, it's nontrivial, because cloning an object calls arbitrary user code and that code might itself try to overwrite the object's contents, which would be unsafe. `safe_cell_exts` implements it only for types with known-safe Clone implementations, not any type. But this could still be in the standard library; it's been proposed at least once. The reason it hasn't been prioritized is that in these situations, people tend to just use RefCell. If you don't know what RefCell is, it's basically a single-threaded read-write lock. With RefCell, you can borrow the interior, but first you have to do a runtime check: if you want an immutable borrow, the object must not be mutably borrowed, and if you want a mutable borrow, it must not be borrowed at all. If the check fails, the program panics. Unlike Cell, which is a 'zero-overhead' construct (Cell<T> is the same size as T and get() compiles to a regular load instruction), RefCell has some time and space overhead: space because it adds a word to track the borrow state, and time because you have to do the check on access. If you're only storing an integer or something, RefCell adds pointless overhead compared to Cell (…not that that stops people from using it anyway). But if you're comparing RefCell<String> to Cell<Rc<String>>, well, the latter has overhead not from the Cell but from the Rc. If you don't actually need reference counting, RefCell<String> is better.
- saagarjha 6y ago> you have an &Cell<String> (aliasing mutable reference to owned string) Is this really an accurate description of what this is? This sounds a lot more like RefCell to me (what would you call that?). Cell seems more like “an aliasing container that holds a particular owned value”, which might sound like gibberish to someone familiar to Rust but makes it clear to me at least that it really doesn’t actually let you grab a reference to the thing inside of it and get away with it.
- comex 6y agoWell, I'm thinking from a sort of low-level "what assembly does it generate" perspective, and from that perspective you can treat `&Cell<T>` almost as its own type of reference, as if it were `&cell T` or something – in other words, a way to represent an aliasing mutable pointer in Rust's type system. After all, you can create a 'Cell reference' to any object, without actually creating a Cell in the first place, by using `Cell::from_mut` to go from `&mut T` to `&Cell<T>`. And all the read/write operations on Cell compile to the same assembly as directly reading or writing a pointer; it's just that the set of operations available is restricted for safety. From that low-level perspective, `&RefCell<String>` is something silly like "a reference to a structure containing a string and also a borrow count". ;p
- aliceryhl 6y agoYes, it is an accurate description of Cell. RefCell is best thought of as a single-threaded RwLock. The Cell type doesn't allow you to get an &mut to the contents because a &mut requires exclusive access to the contents, which requires some sort of locking to do through an aliasing reference. The Cell type also doesn't allow you to get an & to the contents, because a & requires the target to remain immutable for the duration of the reference. For Copy types, Cell provides access through set/get, whereas for non-Copy types you can use swap/set.