The semantic difference is really mainly a consequence of the Copy trait's guarantees: both a copy and a move are identical (always byte copies), so a type implementing Copy says that the source of such a "move/copy" is safe to continue using. That is, that Copy is a valid way to duplicate the value.
Unfortunately that isn't quite enough. For instance, my example fails in the same way with Rc instead of &'a:
// the "magic" type
struct BadClone {
data: i32,
pointer: Rc<Cell<Option<BadClone>>>,
}
impl Clone for BadClone {
fn clone(&self) -> BadClone {
// grab a reference to our internals
let data: &i32 = &self.data;
// what is it?
println!("before: {}", *data);
// now clear out the cell we point to...
self.pointer.set(None);
// print it again (should be no change!)
println!("after: {}", *data);
BadClone { data: self.data, pointer: self.pointer.clone() }
}
}
fn main() {
let cell = Rc::new(Cell::new(None));
cell.set(Some(BadClone {
data: 12345678,
pointer: cell.clone(),
}));
cell.get();
}
(In fact it is slightly worse on the playpen: it also segfaults.)
I think (but don't know, since there's no description from them) @eefriedman's example was using the panic as a proxy for "bad stuff would happen", if the RefCell was replaced with UnsafeCell (as in the real Cell or my reimplementation)---which doesn't have the dynamic checks of RefCell---the problem wouldn't have been caught in such an obvious way, leading to undefined behaviour/memory corruption as in my versions.