Copying is always a raw bitwise copy. This is impossible to implement correctly for types that manage any sort of allocation, because it would lead to a double-free.
Cloning a Box doesn't only clone the inner value, it also creates a separate allocation. Cloning an Arc doesn't just duplicate the pointer, it also increments the reference count.
Indeed. The following code demonstrates a possible side-effect of Arc::clone:
use std::sync::Arc;
fn main() {
let arc = Arc::new(());
//let cloned = arc.clone(); // uncommenting this causes a panic below
let _ = Arc::into_inner(arc).unwrap();
}
While dropping the value contained in the Arc is not a problem if “T: !Drop” (by which you likely actually mean mem::needs_drop::<T>() == false or “T has no drop glue”[1]) an Arc<T> comes with two things that need to be destructed: The value T, and the allocation that holds T and the reference counters. The latter is what prohibits any Arc<T>: Copy implementations in general.
Something you can do with a type T: Copy is to avoid usage of Arc<T> entirely, an just use (and copy) T directly; at least if T isn’t too large for this to be efficient enough.
Right… but the reference counters don’t need to be destructed (they’re just two integers, either in Cells or atomic); and the allocation I mentioned holds bothT and the reference counters together.
Because of the way Arc<T> works, it must always have a destructor, and having a destructor prohibits a type from implementing Copy.
If the cost of incrementing an Arc's reference count is having a tangible impact on your code's performance then it's probably time to take a step back and revisit the design so that a single atomic increment isn't becoming a bottleneck.
Out of curiosity, how did you determine your code spends a lot of time in the Arc::clone() call?
I'm not sure if the OP meant that the CPU spends time or whether they meant it's tedious to type .clone() and declare new variables all time (which I also sometimes find tedious when passing cloned Arcs to closures).
In ancient times Rust used to have a compiler backdoor for Rc and Gc that makes them Copy. The reason for eventually getting rid of that is to make Copy always trivial (pure memcpy calls), which is also why you need a compiler backdoor.
Maybe my poor English makes you confused, but what i'm saying spend a lot of time to clone Arc is, I spend my own time to write it and to fight with compiler, and make block before closure to make a clone.
Note that it is not possible to just add that exception back in today, because the standard library code, and probably other libraries, though I can't name any of them, will often assume that if a type is Copy than it can be simply copied, and the compiler doesn't know anything about that.