It seems that when deriving Clone
, Rust forwards the Clone
trait requirement to Generics that do not require the trait, like if they are wrapped inside an Arc
.
Do I misunderstand how Clone
works or is this a compiler mistake?
Consider the following code, where a.clone()
works, but b.clone()
does not.
Also note that without the b.clone()
call, the code compiles fine, indicating that #[derive(Clone)]
worked.
use std::sync::Arc;
struct Unclonable {}
struct A<T>(Arc<T>);
impl<T> Clone for A<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#[derive(Clone)]
struct B<T>(Arc<T>);
fn main() {
let a = A(Arc::new(Unclonable {}));
let b = B(Arc::new(Unclonable {}));
// Works
a.clone();
// Fails
b.clone();
}
|
3 | struct Unclonable {}
| ----------------- doesn't satisfy `Unclonable: Clone`
...
13 | struct B<T>(Arc<T>);
| --------------------
| |
| method `clone` not found for this
| doesn't satisfy `B<Unclonable>: Clone`
...
22 | b.clone();
| ^^^^^ method cannot be called on `B<Unclonable>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`Unclonable: Clone`
which is required by `B<Unclonable>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `Unclonable` with `#[derive(Clone)]`
|
3 | #[derive(Clone)]
|
When I expand the macros, I see the following generated code:
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2018::*;
#[macro_use]
extern crate std;
use std::sync::Arc;
struct Unclonable {}
struct A<T>(Arc<T>);
impl<T> Clone for A<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
struct B<T>(Arc<T>);
#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::core::clone::Clone> ::core::clone::Clone for B<T> {
#[inline]
fn clone(&self) -> B<T> {
match *self {
B(ref __self_0_0) => B(::core::clone::Clone::clone(&(*__self_0_0))),
}
}
}
fn main() {
let a = A(Arc::new(Unclonable {}));
let b = B(Arc::new(Unclonable {}));
a.clone();
b.clone();
}
What is going on?
Why would the rust compiler add <T: ::core::clone::Clone>
??
Or is this just one of those cases where the intended way is to implement Clone
manually?
CodePudding user response:
This is not a compiler bug, in the sense that it's a documented behavior, even though one might find it weird. The compiler will automatically add constraints to generic types when deriving, even if those constraints are not actually required in the implementation. For instance, deriving Clone
for B<T>
will only implement Clone for B<T>
where T: Clone
, even though it would be possibly to implement that regardless of whether T
is Clone
.
So, for the time being (maybe the compiler will get smarter about these cases in the future), yes, it's one of those cases where you have to implement manually.