Why does Copy require Clone?

The docs point out that the Copy trait requires the implementing type to also implement Clone. Why is that?

It is not strictly necessary for a type to implement Clone via a simple copy. Also, the Copy intrinsic must never actually use the Clone implementation, because it is not allowed to create a reference to the type being copied (see Cell).
As far as I can see, having Clone as a required implementation for anything that should be Copy is a convenience of questionable value, but I fail to see why the compiler requires Clone to implement the Copy intrinsic.

1 Like

Copy trait is defined to have Clone as supertrait.
So every structure must also add the implementation.
No one wants to deal with the language having a type that is Copy but not Clone.
Yes your allowed to write a clone() that does different to copy; why and what depends on you having something obscure to solve.
It might be better to think copy (and move) as happening at a lower level that types.

If the standard library allowed types to implement Copy but not Clone, then some would, perhaps by accident, and then you would not be able to use those types with generic functions (and types and traits) which expect T: Clone.

If Rust had specialization (the ability to pick "more specific" implementations rather than requiring non-overlap) then those generic functions could be written to accept T: Copy or T: Clone, but it does not yet, so the only option (without duplicating code) is T: Clone. So, requiring that all Copy are also Clone ensures that you are never prevented from passing a Copy type to a function wanting a Clone type.

10 Likes

The original reasoning states

It really makes sense for Clone to be a supertrait of Copy -- Copy is a refinement of Clone where memcpy suffices, basically.

And I agree that its main value is in the context of trait bounds.

6 Likes

The value is that “everything that is Copy can be duplicated” is true, so everything that implements Copy can implement Clone, too, so the supertrait relationship is no actual limitation; and on the other hand it then allows you to call functions that generically work with all T: Clone from a generic function that works with a T: Copy which is a significant benefit.

while this is true, you can work the other way around and use the Copy implementation in order to implement clone.

struct Foo(u32);

impl Copy for Foo {}
impl Clone for Foo {
    fn clone(&self) -> Self {
        *self
    }
}

Just because Clone is a supertrait of Copy doesn’t mean that the Clone implementation would need to happen “first”/“independently” from the Copy implementation. It really only states a logical requirement: “if a type implements Copy it also implements Clone” that all types must uphold. Honestly, the only reason why you have to write this Clone implementation yourself is because Rust doesn’t offer any good way to let you not have to do it.

E.g. a generic impl<T: Copy> Clone for T implementation that could save you the boilerplate would be in conflict with generic types Foo<T> trying to implement both Clone for Foo<T> where T: Clone and Copy for Foo<T> where T: Copy.

On the other hand, since Clone implementations can typically be derived, there’s not too much actual boilerplate in practice, so it isn’t all that bad, I suppose.

4 Likes

It's not the compiler per se, and it of course doesn't use Clone to implement Copy. (The actual bound is defined by the standard library.) It just makes sense to have Clone as a supertrait because it's logically a weaker requirement.

What do you think should happen when a type is Copy but not Clone? That seems like a weird thing to do. So here the burden of proving it shouldn't be Clone is on you.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.