Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve compiler error for missing Clone trait bound #76643

Closed
mpizenberg opened this issue Sep 12, 2020 · 3 comments · Fixed by #118076
Closed

Improve compiler error for missing Clone trait bound #76643

mpizenberg opened this issue Sep 12, 2020 · 3 comments · Fixed by #118076
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@mpizenberg
Copy link

I just faced a compiler error when trying to clone a hashmap parameterized by a non-clone type. The error was a bit cryptic, about not expecting a reference, until I realized I've forgot to add the T: Clone trait bound. Here is a minimal function reproducing my issue:

fn clone_map<T>(map1: &HashMap<String, T>) -> HashMap<String, T> {
    map1.clone()
}

And here is the error I got:

7 | fn clone_map<T>(map1: &HashMap<String, T>) -> HashMap<String, T> {
  |                                               ------------------ expected `std::collections::HashMap<std::string::String, T>` because of return type
8 |     map1.clone()
  |     ^^^^^^^^^^^^ expected struct `std::collections::HashMap`, found reference
  |
  = note: expected struct `std::collections::HashMap<std::string::String, T>`
          found reference `&std::collections::HashMap<std::string::String, T>`

The code is available in the playground here. I was wondering if the compiler could deduce that the mistake here could be a missing trait bound and give that hint to the developer.

@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 12, 2020
@scottmcm
Copy link
Member

Hmm, if the code compiled than clippy would help here (https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy), but since it doesn't compile, clippy doesn't get a chance to try 🙁

@quat1024
Copy link

quat1024 commented Jan 29, 2021

Newcomer here - I just ran into the same problem with code that looks like this (this is as much as I can reduce it)

//Oops, I forgot a #[derive(Clone)]
struct MyStruct;

impl MyStruct {
    fn my_clone(&self) -> MyStruct {
        self.clone()
    }
}

leading to

error[E0308]: mismatched types
 --> src/lib.rs:6:9
  |
5 |     fn my_clone(&self) -> MyStruct {
  |                           -------- expected `MyStruct` because of return type
6 |         self.clone()
  |         ^^^^^^^^^^^^ expected struct `MyStruct`, found `&MyStruct`

playground

Here is a particularly confusing error, where rustc is suggesting I remove a mut and add an ampersand to the same mut. Neither of those suggestions make the program compile and run successfully, but deriving Clone does:

//Oops, I forgot a #[derive(Clone)]
struct MyStruct {
    stuff: Vec<u8>
}

impl MyStruct {
    fn clone_then_push_then_get_len(&self) -> usize {
        let mut x = self.clone();
        x.stuff.push(3);
        x.stuff.len()
    }
}

fn main() {
    let my = MyStruct {
        stuff: vec![1, 2]
    };
    
    println!("{}", my.clone_then_push_then_get_len());
    println!("{}", my.stuff.len());
}
warning: variable does not need to be mutable
 --> src/main.rs:8:13
  |
8 |         let mut x = self.clone();
  |             ----^
  |             |
  |             help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` on by default

error[E0596]: cannot borrow `x.stuff` as mutable, as it is behind a `&` reference
 --> src/main.rs:9:9
  |
8 |         let mut x = self.clone();
  |             ----- help: consider changing this to be a mutable reference: `&mut MyStruct`
9 |         x.stuff.push(3);
  |         ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable

playground for that

(This came up when I was trying to write a clone_map function that cloned something, called an fnonce on it, then returned the result, while forgetting to derive Clone on the struct I was implementing it on)


So it's not really related to function trait bounds in specific - I believe the error message is confusing because Clone is implemented for all shared references &T, and rustc thinks you want to call that one.

@estebank
Copy link
Contributor

error[E0308]: mismatched types
 --> src/lib.rs:3:5
  |
2 | fn clone_map<T>(map1: &HashMap<String, T>) -> HashMap<String, T> {
  |                                               ------------------ expected `HashMap<String, T>` because of return type
3 |     map1.clone()
  |     ^^^^^^^^^^^^ expected struct `HashMap`, found reference
  |
  = note: expected struct `HashMap<String, T>`
          found reference `&HashMap<String, T>`
note: `HashMap<String, T>` does not implement `Clone`, so `&HashMap<String, T>` was cloned instead
 --> src/lib.rs:3:5
  |
3 |     map1.clone()
  |     ^^^^
error[E0308]: mismatched types
 --> src/lib.rs:6:9
  |
5 |     fn my_clone(&self) -> MyStruct {
  |                           -------- expected `MyStruct` because of return type
6 |         self.clone()
  |         ^^^^^^^^^^^^ expected struct `MyStruct`, found `&MyStruct`
  |
note: `MyStruct` does not implement `Clone`, so `&MyStruct` was cloned instead
 --> src/lib.rs:6:9
  |
6 |         self.clone()
  |         ^^^^
help: consider annotating `MyStruct` with `#[derive(Clone)]`
  |
2 | #[derive(Clone)]
  |
error[E0596]: cannot borrow `x.stuff` as mutable, as it is behind a `&` reference
 --> src/main.rs:9:9
  |
8 |         let mut x = self.clone();
  |             ----- consider changing this binding's type to be: `&mut MyStruct`
9 |         x.stuff.push(3);
  |         ^^^^^^^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable

I believe only the last case needs some changes.

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Dec 4, 2023
Tweak `.clone()` suggestion to work in more cases

When going through auto-deref, the `<T as Clone>` impl sometimes needs to be specified for rustc to actually clone the value and not the reference.

```
error[E0507]: cannot move out of dereference of `S`
  --> $DIR/needs-clone-through-deref.rs:15:18
   |
LL |         for _ in self.clone().into_iter() {}
   |                  ^^^^^^^^^^^^ ----------- value moved due to this method call
   |                  |
   |                  move occurs because value has type `Vec<usize>`, which does not implement the `Copy` trait
   |
note: `into_iter` takes ownership of the receiver `self`, which moves value
  --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
   |
LL |         for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {}
   |                  ++++++++++++++++++++++++++++++            +
```

When encountering a move error, look for implementations of `Clone` for the moved type. If there is one, check if all its obligations are met. If they are, we suggest cloning without caveats. If they aren't, we suggest cloning while mentioning the unmet obligations, potentially suggesting `#[derive(Clone)]` when appropriate.

```
error[E0507]: cannot move out of a shared reference
  --> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:20:28
   |
LL |     let mut copy: Vec<U> = map.clone().into_values().collect();
   |                            ^^^^^^^^^^^ ------------- value moved due to this method call
   |                            |
   |                            move occurs because value has type `HashMap<T, U, Hash128_1>`, which does not implement the `Copy` trait
   |
note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
  --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
   |
LL |     let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect();
   |                            ++++++++++++++++++++++++++++++++++++++++++++           +
help: consider annotating `Hash128_1` with `#[derive(Clone)]`
   |
LL + #[derive(Clone)]
LL | pub struct Hash128_1;
   |
```

Fix rust-lang#109429.

When encountering multiple mutable borrows, suggest cloning and adding
derive annotations as needed.

```
error[E0596]: cannot borrow `sm.x` as mutable, as it is behind a `&` reference
  --> $DIR/accidentally-cloning-ref-borrow-error.rs:32:9
   |
LL |     foo(&mut sm.x);
   |         ^^^^^^^^^ `sm` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |
help: `Str` doesn't implement `Clone`, so this call clones the reference `&Str`
  --> $DIR/accidentally-cloning-ref-borrow-error.rs:31:21
   |
LL |     let mut sm = sr.clone();
   |                     ^^^^^^^
help: consider annotating `Str` with `#[derive(Clone)]`
   |
LL + #[derive(Clone)]
LL | struct Str {
   |
help: consider specifying this binding's type
   |
LL |     let mut sm: &mut Str = sr.clone();
   |               ++++++++++
```

Fix rust-lang#34629. Fix rust-lang#76643. Fix rust-lang#91532.
@bors bors closed this as completed in f67523d Dec 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants