Deriving a trait results in unexpected compiler er

2019-01-03 03:37发布

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: no method named `clone` found for type `Foo<'a, T>` in the current scope
  --> <anon>:7:9
   |>
16 |>     foo.clone();
   |>         ^^^^^
note: the method `clone` exists but the following trait bounds were not satisfied: `T : std::clone::Clone`
help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `clone`, perhaps you need to implement it:
help: candidate #1: `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?

标签: rust clone
2条回答
神经病院院长
2楼-- · 2019-01-03 04:15

The answer is buried in the error message:

the method clone exists but the following trait bounds were not satisfied: 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.

查看更多
我只想做你的唯一
3楼-- · 2019-01-03 04:20

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.

查看更多
登录 后发表回答