Rust: Improve diagnostics when a type does not impl a trait despite #[derive]'ing it

Created on 3 Mar 2020  路  5Comments  路  Source: rust-lang/rust

_Originally posted by @simonbuchan in https://github.com/rust-lang/rust/issues/26925#issuecomment-593708712_

I think I would be a lot happier with the current situation with "just" adding more information to the compiler diagnostic.

At the moment, you get something like:

error[E0599]: no method named `clone` found for type `Ptr<Foo>` in the current scope
 --> src/main.rs:9:15
  |
2 | struct Ptr<T>(*mut T);
  | ---------------------- method `clone` not found for this
...
9 |     let b = a.clone();
  |               ^^^^^ method not found in `Ptr<Foo>`
  |
  = note: the method `clone` exists but the following trait bounds were not satisfied:
          `Ptr<Foo> : std::clone::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: `std::clone::Clone`

or

error[E0382]: use of moved value: `a`
  --> src/main.rs:10:15
   |
8  |     let a = Ptr(&mut foo);
   |         - move occurs because `a` has type `Ptr<Foo>`, which does not implement the `Copy` trait
9  |     let b = a;
   |             - value moved here
10 |     let ptr = a.0;
   |               ^^^ value used here after move

Neither of these tell you that the reason Ptr doesn't implement Copy or Clone, despite #[derive(Copy, Clone)] being right there. I think some logic to add another sentence for this would get rid of the case where derive doesn't implement when it should, which I think newer users (like me!) would be more likely to hit.

A-diagnostics C-enhancement D-newcomer-roadblock D-terse T-compiler

Most helpful comment

This seems to be better on the latest nightly:

#[derive(Clone)]
struct Ptr<T>(*mut T);

fn foo<T>(ptr: Ptr<T>) {
    ptr.clone()
}

fn main() {}

produces:

error[E0599]: no method named `clone` found for struct `Ptr<T>` in the current scope
 --> bad_clone.rs:5:9
  |
2 | struct Ptr<T>(*mut T);
  | ----------------------
  | |
  | method `clone` not found for this
  | doesn't satisfy `Ptr<T>: std::clone::Clone`
...
5 |     ptr.clone()
  |         ^^^^^ method not found in `Ptr<T>`
  |
  = note: the method `clone` exists but the following trait bounds were not satisfied:
          `T: std::clone::Clone`
          which is required by `Ptr<T>: std::clone::Clone`
help: consider restricting the type parameter to satisfy the trait bound
  |
2 | struct Ptr<T>(*mut T) where T: std::clone::Clone;
  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

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

All 5 comments

This seems to be better on the latest nightly:

#[derive(Clone)]
struct Ptr<T>(*mut T);

fn foo<T>(ptr: Ptr<T>) {
    ptr.clone()
}

fn main() {}

produces:

error[E0599]: no method named `clone` found for struct `Ptr<T>` in the current scope
 --> bad_clone.rs:5:9
  |
2 | struct Ptr<T>(*mut T);
  | ----------------------
  | |
  | method `clone` not found for this
  | doesn't satisfy `Ptr<T>: std::clone::Clone`
...
5 |     ptr.clone()
  |         ^^^^^ method not found in `Ptr<T>`
  |
  = note: the method `clone` exists but the following trait bounds were not satisfied:
          `T: std::clone::Clone`
          which is required by `Ptr<T>: std::clone::Clone`
help: consider restricting the type parameter to satisfy the trait bound
  |
2 | struct Ptr<T>(*mut T) where T: std::clone::Clone;
  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

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

Much better! I think we can close this.

Agreed. It might be nice to put in a reference to #26925, but this is much more likely to get a new user on the right track faster. At worst, they might assume that any impl of Clone must have all parameters be Clone too, but that's a lot easier to learn.

Reopening to track remaining concerns raised by @simonbuchan (either fix or decide not to fix).

I found a case which I feel should trigger the improved error message but doesn't on URLO. I tried to simplify it as much as I could and came up with this:

// main.rs
use std::marker::PhantomData;

#[derive(Clone)]
pub struct Inner<C> {
    phantom: PhantomData<C>,
}

struct Cloner<C> {
    phantom: PhantomData<C>,
}

impl<C> Cloner<C>
where
    C: Clone,
{
    pub fn call_clone(&self, c: &C) -> C {
        c.clone()
    }
}

fn clear_error<C>() {
    let inner: Inner<C> = Inner {
        phantom: PhantomData,
    };
    let cloner = Cloner {
        phantom: PhantomData,
    };
    cloner.call_clone(&inner);
}

fn confusing_error<C>() {
    let inner = Inner {
        phantom: PhantomData,
    };
    let cloner: Cloner<Inner<C>> = Cloner {
        phantom: PhantomData,
    };
    cloner.call_clone(&inner);
}

fn main() {}

clear_error outputs a very clear error message:

error[E0277]: the trait bound `C: std::clone::Clone` is not satisfied
  --> src\main.rs:28:23
   |
28 |     cloner.call_clone(&inner);
   |                       ^^^^^^ the trait `std::clone::Clone` is not implemented for `C`
   |
   = note: required because of the requirements on the impl of `std::clone::Clone` for `Inner<C>`
help: consider restricting type parameter `C`
   |
21 | fn clear_error<C: std::clone::Clone>() {
   |                 ^^^^^^^^^^^^^^^^^^^

while confusing_error's message is less helpful:

error[E0599]: no method named `call_clone` found for struct `Cloner<Inner<C>>` in the current scope
  --> src\main.rs:38:12
   |
4  | pub struct Inner<C> {
   | ------------------- doesn't satisfy `Inner<C>: std::clone::Clone`
...
8  | struct Cloner<C> {
   | ---------------- method `call_clone` not found for this
...
38 |     cloner.call_clone(&inner);
   |            ^^^^^^^^^^ method not found in `Cloner<Inner<C>>`
   |
   = note: the method `call_clone` exists but the following trait bounds were not satisfied:
           `Inner<C>: std::clone::Clone`
Was this page helpful?
0 / 5 - 0 ratings