Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When I can use either Cell or RefCell, which should I choose?

Tags:

rust

From the std::cell documentation, I see that Cell is "only compatible with types that implement Copy". This means I must use RefCell for non-Copy types.

When I do have a Copy type, is there a benefit to using one type of cell over another? I assume the answer is "yes", because otherwise both types wouldn't exist! What are the benefits and tradeoffs of using one type over the other?

Here's a silly, made-up example that uses both Cell and RefCell to accomplish the same goal:

use std::cell::{Cell,RefCell};  struct ThingWithCell {     counter: Cell<u8>, }  impl ThingWithCell {     fn new() -> ThingWithCell {         ThingWithCell { counter: Cell::new(0) }     }      fn increment(&self) {         self.counter.set(self.counter.get() + 1);     }      fn count(&self) -> u8 { self.counter.get() } }  struct ThingWithRefCell {     counter: RefCell<u8>, }  impl ThingWithRefCell {     fn new() -> ThingWithRefCell {         ThingWithRefCell { counter: RefCell::new(0) }     }      fn increment(&self) {         let mut counter = self.counter.borrow_mut();         *counter = *counter + 1;     }      fn count(&self) -> u8 { *self.counter.borrow_mut() } }   fn main() {     let cell = ThingWithCell::new();     cell.increment();     println!("{}", cell.count());      let cell = ThingWithRefCell::new();     cell.increment();     println!("{}", cell.count()); } 
like image 997
Shepmaster Avatar asked May 16 '15 13:05

Shepmaster


People also ask

When to use RefCell Rust?

The RefCell<T> type is useful when you're sure your code follows the borrowing rules but the compiler is unable to understand and guarantee that. Similar to Rc<T> , RefCell<T> is only for use in single-threaded scenarios and will give you a compile-time error if you try using it in a multithreaded context.

What is RefCell t Rust?

RefCell<T> represents the single ownership over the data that it holds. If we use RefCell<T>, then the invariants are enforced at the runtime. RefCell<T> is mainly used in the single-threaded scenario and will give an error if we use in a multithreaded case. RefCell<T> checks the mutable borrows at the runtime.

What is interior mutability in Rust?

Interior mutability is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data: normally, this action is disallowed by the borrowing rules.


2 Answers

I think it is important to take into account the other semantic differences between Cell and RefCell:

  • Cell provides you values, RefCell with references
  • Cell never panics, RefCell can panic

Let us imagine a situation where these differences matter:

let cell = Cell::new(foo); {     let mut value = cell.get();     // do some heavy processing on value     cell.set(value); } 

In this case, if we imagine some complex workflow with a lot of callback and that cell is part of a global state, it is possible that the contents of cell are modified as a side effect of the "heavy processing", and these potential changes will be lost when value is written back in cell.

On the other hand, a similar code using RefCell:

let cell = RefCell::new(foo); {     let mut_ref = cell.borrow_mut().unwrap();     // do some heavy processing on mut_ref } 

In this case, any modification of cell as a side-effect of the "heavy processing" is forbidden, and would result into a panic. You thus are certain that the value of cell will not change without using mut_ref

I would decide which to use depending of the semantics of the value it holds, rather than simply the Copy trait. If both are acceptable, then Cell is lighter and safer than the other, and thus would be preferable.

like image 198
Levans Avatar answered Nov 12 '22 22:11

Levans


You should use Cell, if you can.

Cell uses no runtime checking at all. All it does is an encapsulation that disallows aliasing and tells the compiler that it is an internally mutable slot. In most cases, it should compile to code that is exactly the same as if the type without cell wrapping was there.

By comparison, RefCell uses a simple usage counter to check borrowing vs. mutable borrowing at runtime, and that check can lead to a panic at runtime if you violate for example the exclusivity of mutable borrowing. The possible panic can be an impediment to optimization.

There is at least one more difference. A Cell will never let you get a pointer to the stored value itself. So, if you need that, a RefCell is the only choice.

like image 42
bluss Avatar answered Nov 12 '22 22:11

bluss