Вывод признака приводит к непредвиденной ошибке компилятора, но ручная реализация
Этот код (игровая площадка):
#[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, похоже, предполагает, что компилятор вставляет его неявно, поэтому мое предложение функционально идентично.