Why can Cell in Rust only be used for Copy and not Clone types?

The documentation of the Rust standard library states that Cell can be only used for Copy types and that in all other cases one should use RefCell, but does not explain exactly why.

After studying the documentation and the code of both Cell and RefCell, the only point where it seems to be important is the get function of Cell. If the value is a Copy type then one can just return such a copy. But why is a clone not good enough?

One could directly implement the set function on top of RefCell:

fn set<T>(r: &RefCell<T>, v: T) {
    *r.borrow_mut() = v
}

This only works as long as no one else is holding a reference to the value. But if the value can be cloned, one can just do that:

fn get<T: Clone>(r: &RefCell<T>) -> T {
    r.borrow().clone()
}

Having a type like Cell working with Clone types would avoid the overhead of the run-time borrow checking. Am I missing anything here?

Answers


It's unsound. The comment by DK. is on the right track but you don't even need a panic to cause havoc. One problematic scenario is this:

  1. Cells (together with Option) allow creating cycles, i.e., self-referential types
  2. The Clone implementation gets a &self reference
  3. In the presence of a cycle, the Clone implementation may thus access the cell that's being cloned
  4. Therefore the object being cloned can overwrite itself, while it has an ordinary borrow to itself (namely, &self)
  5. Overwriting while borrowing is unsound because it allows arbitrary type punning and other badness. For example, suppose there's Result<T, E> field that's initially Ok(T), take a reference to the T inside and overwrite the Result with an Err(R). Then the &T suddenly refers to an E value.

Credit for this example goes to Huon Wilson, see the user.rust-lang.org thread Why does Cell require Copy instead of Clone?. His write-up goes into more structural reasons for the restrictions and includes a complete code example, too.


Here is my opinion, but I can't tie it directly to a real reason that such a restriction exists.

I think of a copy as "cheap" (e.g. copying a handful of bits) and a clone as "expensive" (e.g. making a function call or changing data). If such a cell used Clone, it would mandate that the underlying value be duplicated on every use (cell.get()). For example, using a CloneCell<Vec<T>> would mean that every cell.get() would require calling the memory allocator. That's not a good idea.

Restricting to Copy types is thus potentially a way to guide people away from shooting themselves in the foot.


Need Your Help

How to pass arguments to function as then()'s argument in Promise chain with previous resolved value being kept?

javascript es6-promise

I am running JavaScript in node v5.7.1. I am trying to find a smart way to pass arguments to the function as then()'s argument while the previous resolved value still being kept without interfering...

Unable to fix eol whitespace issue in git

git windows-7 github msysgit

I have a repository on guthub that is a fork of fluentmigrator. This was my first foray into git and I've gotten myself into a situation which I am unable to clean up some whitespace conflicts bet...