rust-lang / rust-lang/rust

Borrow checker unhelpful diagnostic

Open
#117,234 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
fn confusing_diagnostic() {
    let r0_members = std::collections::BTreeSet::from(["m0", "m1", "m2", "m3"]);
    let mut rooms = std::collections::BTreeMap::from([(0, ("m0", r0_members))]);
    let for_removal = vec!["m1", "m3"];

    let room = rooms.get_mut(&0).unwrap();
    for_removal
        .iter()
        .filter(|&member| *member != (*room).0)
        .for_each(|member| {
            rooms.get_mut(&0).unwrap().1.remove(member);
        });
}
Current output
error[E0499]: cannot borrow `rooms` as mutable more than once at a time
  --> src/main.rs:24:19
   |
20 |     let room = rooms.get_mut(&0).unwrap();
   |                ----- first mutable borrow occurs here
...
24 |         .for_each(|member| {
   |          -------- ^^^^^^^^ second mutable borrow occurs here
   |          |
   |          first borrow later used by call
25 |             rooms.get_mut(&0).unwrap().1.remove(member);
   |             ----- second borrow occurs due to use of `rooms` in closure
Desired output
error[E0499]: cannot borrow `rooms` as mutable more than once at a time
  --> src/main.rs:24:19
   |
20 |     let room = rooms.get_mut(&0).unwrap();
   |                ----- first mutable borrow occurs here
...
23 |        .filter(|&member| *member != (*room).0)
   |         ------                        ^^^^ first borrow used here
   |         |
   |         first borrow later used by call
...
24 |         .for_each(|member| {
   |                   ^^^^^^^^ second mutable borrow occurs here
25 |             rooms.get_mut(&0).unwrap().1.remove(member);
   |             ----- second borrow occurs due to use of `rooms` in closure
Rationale and extra context

The mention "first borrow later used by call" to for_each is misleading. It would be more helpful to point to the filter closure, as a fix consists in using a reference to the field of room we need in filter instead:

fn fixed() {
    let r0_members = std::collections::BTreeSet::from(["m0", "m1", "m2", "m3"]);
    let mut rooms = std::collections::BTreeMap::from([(0, ("m0", r0_members))]);
    let for_removal = vec!["m1", "m3"];

    let room = rooms.get_mut(&0).unwrap();
    let owner = (*room).0;
    for_removal
        .iter()
        .filter(|&member| *member != owner)
        .for_each(|member| {
            rooms.get_mut(&0).unwrap().1.remove(member);
        });
}

@nirbheek
@sdroege

Other cases

No response

Anything else?

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling the reproducer in src/main.rs and inspect the E0499 borrow-checker diagnostic shown in the issue. Compare the reported spans with the desired output, especially the filter closure; the work is done when the diagnostic points to the first borrow's use there and the existing example is covered by an appropriate compiler test.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.