Skip to content

Unintentionally cloning references can lead to confusing borrow checker errors #48677

Closed
@HexyWitch

Description

@HexyWitch

(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:

fn foo<T: Default>(list: &mut Vec<T>) {
    let mut cloned_items = Vec::new();
    for v in list.iter() {
        cloned_items.push(v.clone())
    }
    list.push(T::default());
    drop(cloned_items);
}

producing the misleading error message:

error[E0502]: cannot borrow `*list` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:5
  |
3 |     for v in list.iter() {
  |              ---- immutable borrow occurs here
...
6 |     list.push(T::default());
  |     ^^^^ mutable borrow occurs here
7 |     drop(cloned_items);
  |          ------------ immutable borrow later used here
  | - immutable borrow ends here

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 of cloned_items is inferred to be Vec<&T>, and the lifetime of the list immutable borrow is extended to the lifetime of cloned_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:

fn foo<T: Default + Clone>(list: &mut Vec<T>) {
    let mut cloned_items = Vec::new();
    for v in list.iter() {
        cloned_items.push(v.clone())
    }
    list.push(T::default());
}

If you give cloned_items an explicit type

let mut cloned_items: Vec<T> = Vec::new();

it gives an error that more clearly explains what you're doing wrong:

error[E0308]: mismatched types
 --> src/main.rs:4:27
  |
4 |         cloned_items.push(v.clone())
  |                           ^^^^^^^^^ expected type parameter, found &T
  |
  = note: expected type `T`
             found type `&T`

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-confusingDiagnostics: Confusing error or lint that should be reworked.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.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