12

Deriving the Clone trait for a struct containing a reference to object of generic type (Unless it has Clone bound. In that case cloning work as expected) will generate clone() method which returns a reference to object but not a new object.

I have the code:

#[derive(Clone)]
struct A<'a, T: 'a>{
    ref_generic: &'a T
}

fn test_call<'a, T: 'a>(a: &A<'a, T>)->A<'a, T>{
    a.clone()
}

Which will cause an error:

error[E0308]: mismatched types
  --> src/lib.rs:15:5
   |
14 | fn test_call<'a, T: 'a>(a: &A<'a, T>)->A<'a, T>{
   |                                        -------- expected `A<'a, T>` because of return type
15 |     a.clone()
   |     ^^^^^^^^^ expected struct `A`, found &A<'_, T>
   |
   = note: expected type `A<'a, T>`
              found type `&A<'_, T>`

Why does derive behave this way?

Following manual implementation allow to avoid this obstacle but unpleasant.

impl<'a, T: 'a> Clone for A<'a, T>{
    fn clone(&self)->Self{
        A{ref_generic: self.ref_generic}
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Pavel Arnold
  • 252
  • 2
  • 9

1 Answers1

12

You are not calling A::clone(). You are calling &A::clone(), i.e. you are cloning the reference, not the object.

The compiler would actually prefer to call A::clone(), because the argument matches more precisely (Clone::clone takes a &self, so the single ref matches exactly, instead of requiring auto-ref for the call to the reference clone), but it can't. (You can see that by looking at the error message you get when you try to do (*a).clone() instead.) This is because the auto-derived Clone naively implements it as:

impl <'a, T: Clone + 'a> Clone for A<'a, T> {
  fn clone(&self) -> Self { Self { ref_generic: self.ref_generic } }
}

Note the Clone bound on T. That bound is not necessary, but the auto-derivation still requires it. This is bug #26925.

Your test_call function does not have a bound on T, which means that the derived Clone impl for A is not available, so the compiler falls back to the only Clone impl it can call, which is the one for &A.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • This means that if you replace `struct A<'a, T: 'a>` with `struct A<'a, T: Clone + 'a>` and `fn test_call<'a, T: 'a>` with `fn test_call<'a, T: Clone + 'a>`, your example will work. – Joeri van Ruth Oct 24 '17 at 11:58