What if the cell itself is set from inside the clone impl of the cell's content? You did get a exclusive and shared reference to the same data concurrently being alive. That is UB.
struct Inner(Option<Rc<Cell<Inner>>>);
impl Clone for Inner {
fn clone(&self) -> Self {
if let Some(inner) = &self.0 {
// Creates a mutable referene to self if self is circular
inner.set(Inner(None));
}
Inner(None)
}
}
fn main() {
let x = Rc::new(Cell::new(Inner(None)));
x.set(Inner(Some(x.clone())));
// this results in UB
// Cell::clone_value(&*x)
}
The whole idea behind Cell is that it can't give out a T& when you have a Cell<T>&. Which is exactly what calling T::clone from Cell<T>::clone_value would require.
Thanks for the example. I'm not sure if I understand correctly but its seem like this problem happens only if the cell contains the the value it is going to clone while cloning the value. That mean if we require Default as the example provided by @SkiFire13 it should be fine.
@SkiFire13's version is safe, but if the clone implementation panics the Cell will be left with the default value and the original value will be lost - to fix that you'd also have to catch_unwind around the clone and make sure to restore the original value - I tried this in Rust Playground but this involves some AssertUnwindSafe that may be dubious.
No need for catch_unwind; you can do this with Drop. See PawnExt in pawn - Rust for a sketch of how this might look.
Using that library, cloning a Cell looks like let x = my_cell.pawn().clone();, which internally works by swapping in Default::default(), cloning the thing of which it now has ownership, and then the Drop on the temporary swaps the original back into my_cell again.