Вывод признака приводит к непредвиденной ошибке компилятора, но ручная реализация

Этот код (игровая площадка):

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

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

... не компилируется:

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`

Добавление use std::clone::Clone; ничего не меняет, как и в прелюдии.

Когда я удаляю #[derive(Clone)] и вручную реализую Clone для Foo, он компилируется как ожидалось!

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

Что здесь происходит?

  • Есть ли разница между #[derive()] -impls и ручными?
  • Является ли это ошибкой компилятора?
  • Что-то еще, о чем я не думал?

Ответ 1

Ответ выгружается в сообщении об ошибке:

существует метод clone, но следующие оценки характеристик не выполнялись: T : std::clone::Clone

Когда вы получаете clone (и многие другие автоматически производные типы), он добавляет привязку clone к всем общим типам. Используя rustc -Z unstable-options --pretty=expanded, мы можем видеть, что он делает:

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)),},
        }
    }
}

В этом случае оценка не нужна, потому что общий тип находится за ссылкой.

Теперь вам нужно будет реализовать clone самостоятельно. Есть проблема Rust для этого, но это сравнительно редкий случай с обходным путем.

Ответ 2

В вашем примере вы получите Clone без каких-либо проблем, если вы явно отметите, что T должен реализовать Clone, например:

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

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

(ссылка для игровой площадки)

Кажется необычным, что вы можете явно не указывать привязку, но ответ Shepmaster, похоже, предполагает, что компилятор вставляет его неявно, поэтому мое предложение функционально идентично.