Overriding Copy to probe it?

I have got a problem with the Copy trait :

pub trait Copy: Clone { }

Since there is no function, there is no placeholder to insert some println!() in order to probe the implicit calls.

What is also puzzling me is that the doc makes me implement Clone as well; but even though there is a clone() method, it does not seem to be called.

There are several intricated questions:

  • is it possible to probe calls to the default Copy implementation ?
  • is clone() ever implicitely called by some Copy mechanisms ?
struct A {
  x:i64
}

impl A {

fn new() -> Self {
  println!("A::new()");
  A {x:0}
}

fn f(&self) {
  println!("A::f() => {}", self.x);
}

}

impl Copy for A {}

impl Clone for A {
  
fn clone(&self) -> Self {
  println!("A::clone()");
  return A {x:self.x.clone()};
}

}

struct B {
  x:String
}

impl B {

fn new() -> Self {
  println!("B::new()");
  B {x:String::from("B")}
}

fn f(&self) {
  println!("B::f() => {}", self.x);
}

}

//impl Copy for B {}//impossible

impl Clone for B {
  
fn clone(&self) -> Self {
  println!("B::clone()");
  return B {x:self.x.clone()};
}

}

fn main() {
  let a1 = A::new();
  let a2 = a1;//does not call any println!()
  a2.f();

  let b1 = B::new();
  let b2 = b1;//does not call any println!()
  b2.f();
}

No, it's effectively a memcpy. There are no copy (or move) constructors. If you need to track it, implement Clone but not Copy.

If a type is Copy, you can always call Clone::clone explicitly (since Copy requires Clone). But if you're aware the type was Copy, you generally wouldn't.

Ideally the Clone implementation of a type that also implements Copy should also just effectively be a memcpy, but there's no way to enforce that, and so it doesn't have to be. Some optimizations may do a copy instead of calling clone.

3 Likes

No, because there's no call and no default implementation. Copy is just a marker that means "you can copy this type by just copying its bytes", which then extends into the ownership system by allowing Copy types that have been moved to still be accessible. In both cases the action is copying bytes, which is outside the scope of your type.

No, again because there's no copy. Actually the opposite can happen: the stdlib can avoid calling clone and instead do a byte copy if your type implements Copy.

3 Likes

Considering that, I don't understand why Copy requires Clone.
If a type was Copy but not Clone, there would be a guarantee that it is "cheap" to copy.

Copy is a specialized Clone. It'd be odd if you couldn't pass Copy types to functions/methods/generic implementations that have a Clone bound.

2 Likes

Even if something is Copy, that doesn't mean it's cheap to copy. A 4kB fixed size array is expensive to copy no matter what you do.

If your concern is that having both means generic code might end up using the "expensive" Clone when the "cheap" Copy is available, don't be. If you derive both Copy and Clone together, the generated Clone just directly defers to a simple Copy operation.

2 Likes

It does have to be, breaking that is breaking the API contract of Clone. It is not checked, though, as you said.

Clippy does have a lint for that, though.

(This is pretty much a comment about wording.)

It doesn't have to be,[1] because you can do something entirely different in safe code :slight_smile:. And enforcing it with something you can't disable would be a breaking change.

Definitely it should be, and you can't rely on clone being called when you may expect (if you also implement Copy) due to specializing optimizations. If you try to rely on them being different, you will have bugs. On the other hand, code can't rely on them being the same for soundness specifically.

It's similar to having a consistent Hash or Ord implementation in that respect.


  1. "should" is not "must" ↩ī¸Ž

So an another one...

The PR that introduced this wording was #33420, and it says:

I tried to use "should" and "must" as defined here.

Which defines "should" as:

This word, or the adjective "RECOMMENDED", mean that there
may exist valid reasons in particular circumstances to ignore a
particular item, but the full implications must be understood and
carefully weighed before choosing a different course.

So yes, it is not exactly the same, but I'll will argue that there it is very rare that you actually need to break this and/or understand the full implications.

Indeed.

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.