0

I'm trying to compile following rust code:

#[derive(Clone)]
struct Foo<Data> {
    f: fn(&Data),
}

trait Trait : Clone {
    type DataType;
}

// throws:
// error[E0277]: the trait bound `Data: std::clone::Clone` is not satisfied
impl<Data> Trait for Foo<Data> {
    type DataType = Data;
}

It complains about Data generic argument not satisfying Clone constraint, even though it is not supposed to.

From my understanding Foo<Data> should support Clone without need for Data to support it.

What am I doing wrong?

FLashM
  • 23
  • 4

1 Answers1

0

The derive for Clone sometimes adds where constraints that are restrictive. As of May 2020, it is still an open bug. See: #[derive] sometimes uses incorrect bounds · Issue #26925 · rust-lang/rust.

A workaround is to implement Clone manually for this struct. For example:

impl<Data> Clone for Foo<Data> {
    fn clone(&self) -> Self {
        Self { f: self.f }
    }
}
Dan Aloni
  • 3,968
  • 22
  • 30
  • Thanks a lot. It is a bit weird, though. Problem only arises when I add `impl Trait`. Without it derive succeeds just fine. Do you by any chance know of any workarounds that don't require manual clone? – FLashM May 05 '20 at 17:57
  • The derive's impl has `where Data: Clone`, and that on its own is okay (despite it being a `derive` issue in this specific case). Now it is important to understand because of this, `Foo: Clone` does not hold unless `Data : Clone` as well. So far so good. Now for the trait and the `impl`. Because we have `Trait : Clone`, then the `impl` demands `Foo: Clone`, and therefore `Data: Clone`. However, the `impl` does not restrict for `Data: Clone`, and therefore the failure. – Dan Aloni May 05 '20 at 18:54
  • Why didn't it fail right away on struct definition? Because Data is not constrained there either. – FLashM May 06 '20 at 23:11
  • There's nothing in the struct definition itself that requires `Clone` from `Data`, therefore it is remains valid. Say, if you have a `struct Bar {}` that is does not implement `Clone`, you can declare `let x : Foo = Foo { f: |_| {} };` and it will compile. However if you try to add `let y = x.clone();` it will not build because `Foo: Clone` does not hold. – Dan Aloni May 13 '20 at 19:26
  • Ah, I see. I think I understand. Derive only adds implementation if all the members support Clone. I was under the impression that it's unconditional. Thanks. – FLashM May 15 '20 at 02:34
  • It's unconditional, but the type logic in the compiler only considers the implementation for types for which the implementation is relevant, based on `where`. – Dan Aloni May 15 '20 at 17:49