But this definition is circular. The implementation of Clone requires Copy, because it moves out of a shared reference. But the definition of Copy requires Clone, because it is a super trait.
It doesn't seem like this should be allowed. I also don't know how it makes sense in the general case, especially if the type stores allocations. Is the copy just a bit-wise memcpy? Couldn't that cause safety issues? I must be missing something.
Yes, Copy is akin to memcpy.
It requires clone, but it doesn't call it or need it.
In order for a type to be Copy, all of its members also have to be Copy, so you won't have a problem with allocated heap types (since they're not Copy)
I had assumed Copy existed purely at the language level and simply desugared to object.clone(). If that were true, then the example would be circular and invalid. But I guess Copy must be handled as a special case by the compiler and is always a bitwise copy.
Doesn't that mean the output of Copy and Clone could be different? There's no mechanism to prevent that, right? That just seems a little counter-intuitive to me.
Ah that's in the Clone documentation. I was reading the Copy documentation.
Why not reverse the dependency then? Couldn't you have Copy as a marker trait and a blanket implementation of Clone for types that are Copy? That would still allow users to implement Clone without Copy, but it would prevent any breakage of the invariant.
Yes, this would be desirable, but due to limitations in Rust's trait system, it would prevent things like other generic implementations of Clone. For example, we would now have two conflicting generic impls that both apply to Option<u8>:
impl<T: Copy> Clone for T { ... }
impl<T: Clone> Clone for Option<T> { ... }
Edit: These would conflict because of this other existing impl which we would still want to have:
It's a problem for Option, because it means that Option<i32> can't be Copy2, only Clone2.
If you change the impls you can make it so instead Option<i32> is Copy2 and Clone2, but then Option<String> can't be Clone2 because String isn't Copy2.
@mbrubeck, it might be helpful to edit your post to include the missing impl that makes it fail to compile. It wasn't immediately obvious to me, so I think it might not be to others who find this in the future. Thanks for explaining. And thanks to @trentj for clarifying.
The circularity is okay not just because Copy is special. You can use the implementation of a subtrait to implement the supertrait in general. Another typical example of this is Ord which requires PartialOrd, where usually PartialOrd is implemented using Ord: