Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deriving a trait results in unexpected compiler error, but the manual implementation works

Tags:

clone

rust

This code (playground):

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

fn bar<'a, T>(foo: Foo<'a, T>) {
    foo.clone();
}

... does not compile:

error[E0599]: no method named `clone` found for struct `Foo<'a, T>` in the current scope
   --> src/main.rs:16:9
    |
3   | struct Foo<'a, T: 'a> {
    | ---------------------
    | |
    | method `clone` not found for this
    | doesn't satisfy `Foo<'_, T>: std::clone::Clone`
...
16  |     foo.clone();
    |         ^^^^^ method not found in `Foo<'a, T>`
    |
    = note: the method `clone` exists but the following trait bounds were not satisfied:
            `T: std::clone::Clone`
            which is required by `Foo<'_, T>: std::clone::Clone`
help: consider restricting the type parameter to satisfy the trait bound
    |
3   | struct Foo<'a, T: 'a> where T: std::clone::Clone {
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^

Adding use std::clone::Clone; doesn't change anything, as it's already in the prelude anyway.

When I remove the #[derive(Clone)] and manually implement Clone for Foo, it compiles as expected!

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

What is going on here?

  • Is there a difference between #[derive()]-impls and manual ones?
  • Is this a compiler bug?
  • Something else I didn't think of?
like image 910
Lukas Kalbertodt Avatar asked Sep 09 '16 15:09

Lukas Kalbertodt


2 Answers

The answer is buried in the error message:

    = note: the method `clone` exists but the following trait bounds were not satisfied:
            `T: std::clone::Clone`
            which is required by `Foo<'_, T>: std::clone::Clone`

When you derive Clone (and many other automatically-derived types), it adds a Clone bound on all generic types. Using rustc -Z unstable-options --pretty=expanded, we can see what it becomes:

impl <'a, T: ::std::clone::Clone + 'a> ::std::clone::Clone for Foo<'a, T> {
    #[inline]
    fn clone(&self) -> Foo<'a, T> {
        match *self {
            Foo { t: ref __self_0_0 } =>
            Foo{t: ::std::clone::Clone::clone(&(*__self_0_0)),},
        }
    }
}

In this case, the bound is not needed because the generic type is behind a reference.

For now, you will need to implement Clone yourself. There's a Rust issue for this, but it's a comparatively rare case with a workaround.

like image 139
Shepmaster Avatar answered Nov 20 '22 14:11

Shepmaster


Your example will derive Clone without any problems if you explicitly mark that T should implement Clone, like this:

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

fn bar<'a, T: Clone>(foo: Foo<'a, T>) {
    foo.clone();
}

(Playground link)

It seems unusual that you can avoid specifying the bound explicitly, but Shepmaster's answer seems to suggest that the compiler inserts it implicitly, so my suggestion is functionally identical.

like image 33
Aurora0001 Avatar answered Nov 20 '22 14:11

Aurora0001