Skip to content

Improve error message around unexpected derive bounds #143714

Open
@josephg

Description

@josephg

Code

use std::sync::Arc;

// Not Clone
struct Foo;

#[derive(Clone)]
struct Bar<T>(Arc<T>);

fn main() {
    let x = Bar(Arc::new(Foo));
    x.clone(); // Error!
}

Current output

error[E0599]: the method `clone` exists for struct `Bar<Foo>`, but its trait bounds were not satisfied
--> src/main.rs:11:7
   |
4  | struct Foo;
   | ---------- doesn't satisfy `Foo: Clone`
...
7  | struct Bar<T>(Arc<T>);
   | ------------- method `clone` not found for this struct because it doesn't satisfy `Bar<Foo>: Clone`
...
11 |     x.clone(); // Error!
   |       ^^^^^ method cannot be called on `Bar<Foo>` due to unsatisfied trait bounds
   |
note: trait bound `Foo: Clone` was not satisfied
  --> src/main.rs:6:10
   |
6  | #[derive(Clone)]
   |          ^^^^^ unsatisfied trait bound introduced in this `derive` macro
help: consider annotating `Foo` with `#[derive(Clone)]`
   |
4  + #[derive(Clone)]
5  | struct Foo;
   |

For more information about this error, try `rustc --explain E0599`.

Desired output

note: The implementation of Clone provided by #[derive(Clone)] on struct Bar
has a trait bound of "T: Clone". This may be overly restrictive. If the constraint
is incorrect, manually write an implementation of Clone for Bar without this
trait bound. For example:

impl Clone for Bar {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

Rationale and extra context

Its quite surprising to many people that #[derive(Clone)] on a generic struct Foo<T> creates a trait implementation with a bound of T: Clone. Clone is trivially implementable for any type where all the fields implement Clone. The generic parameter itself is irrelevant.

You can - for example - have structs which can trivially be Cloned but where the trait is not clone (as in the example above). Or the inverse - for example, when a field is of type Cell<T> or Mutex<T>.

Currently the error does not sufficiently hint that this is the case. I personally spent half an hour fighting the compiler the first time I ran into it - I was confused how my #[derive(Clone)] was seemingly being ignored. There was a recent blog post about this issue which gathered over 100 comments on HN. Others are clearly running into this problem too.

The error message suggests the problem is with the lack of impl Clone for T. But this is a distraction. The right fix is often to manually implement Clone.

This blog post has some backstory on this choice within derive itself, and talks about how we might fix derive itself such that it doesn't have this pitfall. Unfortunately, doing so may have semver implications.

In the meantime, it'd be great to fix rustc's diagnostic message to at least point people in the right direction.

The diagnostic above should probably be added when all of these conditions are met:

  1. .clone() is called (or default(), eq(), etc)
  2. The type does not implement clone
  3. There is a #[derive(Clone)] on the type
  4. The type has generic parameters

Other cases

I've used Clone here an example, but the exact same problem shows up with other built in derive macros. For example, PartialEq, Default, Debug and so on.

Ideally, we should improve the diagnostics in all of these cases.

Rust Version

rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: x86_64-unknown-linux-gnu
release: 1.87.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)D-confusingDiagnostics: Confusing error or lint that should be reworked.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions