Why do these `Clone` and `Copy` derives not work?

When I manually implement Clone and Copy they work:

use std::fmt::Debug;

// #[derive(Debug, Clone, Copy)]
#[derive(Debug)]
struct Refs<'a, A, B> where &'a A: Copy, &'a B: Copy {
    a: &'a A,
    b: &'a B,
}

impl<'a, A, B> Clone for Refs<'a, A, B> {
    fn clone(&self) -> Self {
        Self {
            a: self.a,
            b: self.b,
        }
    }
}

impl<'a, A, B> Copy for Refs<'a, A, B> {}

trait Take {
    fn take(self);
}

impl<'a, A: Debug, B: Debug> Take for Refs<'a, A, B> {
    fn take(self) {
        println!("took {self:?}");
    }
}

#[derive(Debug)]
struct Hi {
    a: f32,
}

#[derive(Debug)]
struct Bye {
    a: f32,
}

fn main() {
    let a = Hi { a: 1.0 };
    let b = Bye { a: 2.0 };
    let refs = Refs { a: &a, b: &b };
    refs.take();
    refs.take();
}

(Playground)

Output:

took Refs { a: Hi { a: 1.0 }, b: Bye { a: 2.0 } }
took Refs { a: Hi { a: 1.0 }, b: Bye { a: 2.0 } }

When I try to #[derive] them I get errors:

use std::fmt::Debug;

#[derive(Debug, Clone, Copy)]
// #[derive(Debug)]
struct Refs<'a, A, B> where &'a A: Copy, &'a B: Copy {
    a: &'a A,
    b: &'a B,
}

/*
impl<'a, A, B> Clone for Refs<'a, A, B> {
    fn clone(&self) -> Self {
        Self {
            a: self.a,
            b: self.b,
        }
    }
}

impl<'a, A, B> Copy for Refs<'a, A, B> {}
*/

trait Take {
    fn take(self);
}

impl<'a, A: Debug, B: Debug> Take for Refs<'a, A, B> {
    fn take(self) {
        println!("took {self:?}");
    }
}

#[derive(Debug)]
struct Hi {
    a: f32,
}

#[derive(Debug)]
struct Bye {
    a: f32,
}

fn main() {
    let a = Hi { a: 1.0 };
    let b = Bye { a: 2.0 };
    let refs = Refs { a: &a, b: &b };
    refs.take();
    refs.take();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `refs`
  --> src/main.rs:48:5
   |
46 |     let refs = Refs { a: &a, b: &b };
   |         ---- move occurs because `refs` has type `Refs<'_, Hi, Bye>`, which does not implement the `Copy` trait
47 |     refs.take();
   |          ------ `refs` moved due to this method call
48 |     refs.take();
   |     ^^^^ value used here after move
   |
note: `Take::take` takes ownership of the receiver `self`, which moves `refs`
  --> src/main.rs:24:13
   |
24 |     fn take(self);
   |             ^^^^
help: you could `clone` the value and consume it, if the following trait bounds could be satisfied: `Hi: Clone` and `Bye: Clone`
   |
47 |     refs.clone().take();
   |         ++++++++
help: consider annotating `Hi` with `#[derive(Clone)]`
   |
34 + #[derive(Clone)]
35 | struct Hi {
   |
help: consider annotating `Bye` with `#[derive(Clone)]`
   |
39 + #[derive(Clone)]
40 | struct Bye {
   |

For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` (bin "playground") due to 1 previous error

My question is why? Could this be a bug? Is there a reason why Copy derive is limited?

Plus Clone can be simplified to this completely trivial impl:

impl<'a, A, B> Clone for Refs<'a, A, B> {
    fn clone(&self) -> Self {
        *self
    }
}

This is expected behavior by design, currently. There’s often been discussions on possible improvements. Just see how long e.g. this [almost decade-old] GitHub issue about it has gotten already.

If you want more convenient derive that behaves differently, you can use third-party crates. E.g. derive_where::derive_where or derive(educe::Educe) or impl_tools::autoimpl would be a macros that offer custom syntax for manually specifying alternative bounds.

1 Like

Refs must be Copy for it to be consumed twice by take -- it must be copied for that to work. The derived Copy will require that A and B are also Copy (it is very simplistic), and Hi and Bye are not Copy. If you derive Copy and Clone for Hi and Bye, the error will go away.

The main reasons are historical limitations, not breaking existing code, and semver hazards. Here's a related article. If we get perfect derive we'll have to keep "trivially-forwarded-the-bounds derive" in some form too, for backwards compatibility.

1 Like

Forgive the question, but when is &T: Copy not satisfied? (are references only Clone)

The derive of Copy is very simplistic/limited. It simply requires that all generic type params (A and B) are also Copy. It doesn't matter that you're actually only storing &A and &B in the struct and that your bounds are for these types, because the derive macro only looks at the generic types of the struct.

1 Like

Gotcha, so the derive issue in the OP remains, even if we remove the where &A: Copy bounds on the struct definition. That makes more sense!

It's always satisfied.

1 Like

Another thing that's interesting is that you can define the struct more generically so it just stores A and B, without any bounds or references. Then when the struct is used, the types given are &Hi for A and &Bye for B, which do implement Copy since all (immutable) references implement Copy. This satisfies the Copy bound that the Copy derivation for Refs requires.

So the following works. Of course, it may or may not work in your actual program, which I assume is more complex.

use std::fmt::Debug;

#[derive(Debug, Clone, Copy)]
struct Refs<A, B> {
    a: A,
    b: B,
}

trait Take {
    fn take(self);
}

impl<A: Debug, B: Debug> Take for Refs<A, B> {
    fn take(self) {
        println!("took {self:?}");
    }
}

#[derive(Debug)]
struct Hi {
    a: f32,
}

#[derive(Debug)]
struct Bye {
    a: f32,
}

fn main() {
    let a = Hi { a: 1.0 };
    let b = Bye { a: 2.0 };
    let refs = Refs { a: &a, b: &b };
    refs.take();
    refs.take();
}

playground

Incidentally, it is typical in Rust to not use unnecessary bounds on generic types for the struct itself, since they have to be repeated on the impl blocks anyway.

1 Like

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.