rust-lang / rust-lang/rust

Borrow checker wrongly counts mutable references

Open
#148,532 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-borrow-checker C-bug fixed-by-polonius T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code (playground):

pub struct MyCollection(Vec<(String, Record)>);
#[derive(Default)]
pub struct Record {}

impl MyCollection {
    /// retrieves a record with given key, or creates a new one and appends it to
    /// the list if it doesn't exist yet
    pub fn get_or_push_default<'a>(&'a mut self, key: &str) -> &'a mut Record {
        // try to find a record with given key
        {
            if let Some((_key, record)) = self.0.iter_mut().find(|(k, _)| k == key) {
                return record;
            }
        } // `self.0` is no longer mutably referenced

        // otherwise, create a default record and push it to the vec
        let record = Record::default();
        // ERROR: cannot borrow `self.0` as mutable more than once at a time
        // (although it isn't borrowed)
        self.0.push((key.to_owned(), record));
        // with push_mut we wouldn't need unwrap, but that's not the issue here
        &mut self.0.last_mut().unwrap().1
    }
}

I expected this to compile. Not at any point in the code there is more than 1 mutable reference to self.0.

Instead, borrow checker detects that self.0 is mutably borrowed more than once when I call self.0.push(..) (or self.0.last_mut()). It identifies the first mutable reference in self.0.iter_mut(), although I surrounded it by braces which should have created an explicit scope in which self.0 is mutably borrowed.

Compiler error message:

Error message

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `self.0` as mutable more than once at a time
  --> src/lib.rs:20:9
   |
 8 |     pub fn get_or_push_default<'a>(&'a mut self, key: &str) -> &'a mut Record {
   |                                -- lifetime `'a` defined here
...
11 |             if let Some((_key, record)) = self.0.iter_mut().find(|(k, _)| k == key) {
   |                                           ------ first mutable borrow occurs here
12 |                 return record;
   |                        ------ returning this value requires that `self.0` is borrowed for `'a`
...
20 |         self.0.push((key.to_owned(), record));
   |         ^^^^^^ second mutable borrow occurs here

error[E0499]: cannot borrow `self.0` as mutable more than once at a time
  --> src/lib.rs:22:14
   |
 8 |     pub fn get_or_push_default<'a>(&'a mut self, key: &str) -> &'a mut Record {
   |                                -- lifetime `'a` defined here
...
11 |             if let Some((_key, record)) = self.0.iter_mut().find(|(k, _)| k == key) {
   |                                           ------ first mutable borrow occurs here
12 |                 return record;
   |                        ------ returning this value requires that `self.0` is borrowed for `'a`
...
22 |         &mut self.0.last_mut().unwrap().1
   |              ^^^^^^ second mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` (lib) due to 2 previous errors

Meta

rustc --version --verbose:

rustc 1.90.0 (1159e78c4 2025-09-14)
binary: rustc
commit-hash: 1159e78c4747b02ef996e55082b704c09b970588
commit-date: 2025-09-14
host: aarch64-unknown-linux-gnu
release: 1.90.0
LLVM version: 20.1.8

I also tested on latest stable, beta and nightly channels available on Rust playground.

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

Reproduce the supplied get_or_push_default example in the Rust Playground across stable, beta, and nightly, then trace borrow-checker handling of the explicit scope and returned mutable reference. A complete fix should accept this example without E0499 while preserving errors for genuinely overlapping mutable borrows; no repository file or test is named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.