(In the example) why A should also implement Clone and not just B (i.e. <A as Trait>::T)

playground

trait Trait {
    type Sub;
}

struct A;

#[derive(Clone)]
struct B;

impl Trait for A {
    type Sub = B;
}

#[derive(Clone)]
struct C<T: Trait>(T::Sub);

fn main() {
    let x = C::<A>(B);
    x.clone();
}

Such compiler behavior reminds me of PhantomData: it "acts like" it own a T, which in this case it doesn't. Further tests show that even if Clone is implemented for A and compiled, A's clone is never called when calling clone on C.
BTW, can I use PhantomData for cases that don't really own a value of some type, such as a read handle which deserialize the output to some type? This is the case with mongodb's Collection, which uses PhantomData.

What gives you the impression that A needs to be cloned in your example? C never has an instance of A, and the trait doesn't provide a constructor of any kind, so how could it possibly call clone on A?

Yes, any time you need a type parameter but don't store a value of that type directly inside it PhatomData is the simplest solution.

It's not my impression, it's a compiler error. I forgot to paste it just now, sorry.

   Compiling playground v0.0.1 (/playground)
error[E0599]: the method `clone` exists for struct `C<A>`, but its trait bounds were not satisfied
  --> src/main.rs:19:7
   |
5  | struct A;
   | -------- doesn't satisfy `A: Clone`
...
15 | struct C<T: Trait>(T::Sub);
   | ------------------
   | |
   | method `clone` not found for this struct
   | doesn't satisfy `C<A>: Clone`
...
19 |     x.clone();
   |       ^^^^^ method cannot be called on `C<A>` due to unsatisfied trait bounds
   |
note: trait bound `A: Clone` was not satisfied
  --> src/main.rs:14:10
   |
14 | #[derive(Clone)]
   |          ^^^^^ unsatisfied trait bound introduced in this `derive` macro
   = note: the following trait bounds were not satisfied:
           `A: Clone`
           which is required by `C<A>: Clone`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `clone`, perhaps you need to implement it:
           candidate #1: `Clone`
help: consider annotating `A` with `#[derive(Clone)]`
   |
5  | #[derive(Clone)]
   |

For more information about this error, try `rustc --explain E0599`.
error: could not compile `playground` due to previous error

Ahh right of course.

The derive for Clone assumes all the type parameters are used in the type, or could be in the future.

You can read a bit more about why this is a trickier problem to solve than it might seem here

https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/

Specifically "perfect derive" is what would fix the issue you're running into here.

The simple fix is just to manually implement Clone.

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.