-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Unintentionally cloning references can lead to confusing borrow checker errors #48677
Comments
Clippy has a clone-on-copy lint, moving it into rustc would be a way to push code like this in the right direction. |
Triage: the original example now compiles on 2018 edition.
|
Just to be clear, I don't think NLL really has anything to do with it, a simple modification makes the example not compile on rust 2018 edition:
whereas ofc this compiles:
|
Thank you for pointing that out, kyren. There was another issue I ran into during triage that only triggered the error when used, and I mistagged this. Enabling nll feature on 2015 does change if an error is produced (before use), but it does not imply a fix (nor does simply compiling imply one either, which is why I made a seperate issue to be reviewed in #57964). |
and for the sake of update, when using kyren's first example, is
which is not much change except for the spans in diagnostics. |
Is this going to be fixed ? This is a pretty big fail imho. The error message is downright misleading. I’ve been programming in rust for over 3 years and this took me about 20-30 minutes to figure out. In the issue I gave, which was closed and is more simpler imho with no user level generics at all, it highlights what’s particularly bothersome about this issue, which is that for any of the types used in the hashmap, for example might be a complex nesting of types, if a single one of them doesn’t have clone (which can be very hard to track down especially if it’s external code using external code) you will receive the essentially useless error message that we see today. I don’t know immediately the fix here. It seems like a hard problem and so I think this needs to be escalated so we can get more eyes on it. But in as strong language as I think is appropriate: the current status quo error message here is actively hostile to new and older users and this should be given a more serious treatment. This is a clear demerit on rusts reputation for having stellar error messages. We can do better. |
Making this error in particular better would be hard, but I think there's an alternative: have an early deny by default lint on all |
With #80723, we will start linting on calling |
Detect calls to .clone() on T: !Clone types on borrowck errors When encountering a lifetime error on a type that *holds* a type that doesn't implement `Clone`, explore the item's body for potential calls to `.clone()` that are only cloning the reference `&T` instead of `T` because `T: !Clone`. If we find this, suggest `T: Clone`. ``` error[E0502]: cannot borrow `*list` as mutable because it is also borrowed as immutable --> $DIR/clone-on-ref.rs:7:5 | LL | for v in list.iter() { | ---- immutable borrow occurs here LL | cloned_items.push(v.clone()) | ------- this call doesn't do anything, the result is still `&T` because `T` doesn't implement `Clone` LL | } LL | list.push(T::default()); | ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | drop(cloned_items); | ------------ immutable borrow later used here | help: consider further restricting this bound | LL | fn foo<T: Default + Clone>(list: &mut Vec<T>) { | +++++++ ``` ``` error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 | LL | fn qux(x: A) { | - binding `x` declared here LL | let a = &x; | -- borrow of `x` occurs here LL | let b = a.clone(); | ------- this call doesn't do anything, the result is still `&A` because `A` doesn't implement `Clone` LL | drop(x); | ^ move out of `x` occurs here LL | LL | println!("{b:?}"); | ----- borrow later used here | help: consider annotating `A` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct A; | ``` Fix rust-lang#48677.
Rollup merge of rust-lang#122254 - estebank:issue-48677, r=oli-obk Detect calls to .clone() on T: !Clone types on borrowck errors When encountering a lifetime error on a type that *holds* a type that doesn't implement `Clone`, explore the item's body for potential calls to `.clone()` that are only cloning the reference `&T` instead of `T` because `T: !Clone`. If we find this, suggest `T: Clone`. ``` error[E0502]: cannot borrow `*list` as mutable because it is also borrowed as immutable --> $DIR/clone-on-ref.rs:7:5 | LL | for v in list.iter() { | ---- immutable borrow occurs here LL | cloned_items.push(v.clone()) | ------- this call doesn't do anything, the result is still `&T` because `T` doesn't implement `Clone` LL | } LL | list.push(T::default()); | ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | drop(cloned_items); | ------------ immutable borrow later used here | help: consider further restricting this bound | LL | fn foo<T: Default + Clone>(list: &mut Vec<T>) { | +++++++ ``` ``` error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 | LL | fn qux(x: A) { | - binding `x` declared here LL | let a = &x; | -- borrow of `x` occurs here LL | let b = a.clone(); | ------- this call doesn't do anything, the result is still `&A` because `A` doesn't implement `Clone` LL | drop(x); | ^ move out of `x` occurs here LL | LL | println!("{b:?}"); | ----- borrow later used here | help: consider annotating `A` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct A; | ``` Fix rust-lang#48677.
(Note from pnkfelix: I have updated the example to continue illustrating the issue even in the presence of NLL.)
This issue is a result of all references implementing Clone. The following code will not compile:
producing the misleading error message:
Because Clone is not a trait bound on T, where you'd expect
v.clone()
to return a cloned T it's actually returning a cloned &T. The type ofcloned_items
is inferred to beVec<&T>
, and the lifetime of thelist
immutable borrow is extended to the lifetime ofcloned_items
. This is hard to figure out from the error message. The solution is to add Clone as a trait bound to T. The following code builds:If you give cloned_items an explicit type
it gives an error that more clearly explains what you're doing wrong:
I'm not sure what my proposed fix would be. I don't know if there are any use cases for explicitly cloning a reference. Ideally you'd get a warning on cloning a reference to a type that doesn't implement Clone.
The text was updated successfully, but these errors were encountered: