Skip to content

multiple mutable borrow error with get_mut #75112

Closed as not planned
Closed as not planned
@95th

Description

@95th

I tried this code:

use std::collections::HashMap;

pub fn find<'a>(lookup: &HashMap<u32, usize>, items: &'a mut Vec<u32>, id: u32) -> Option<&'a mut u32> {
    if let Some(token) = lookup.get(&id) {
        return items.get_mut(*token);
    }

    if let Some(token) = lookup.get(&id.wrapping_add(1)) {
        let item = items.get_mut(*token)?;
        if *item == id {
            return Some(item);
        }
    }

    if let Some(token) = lookup.get(&(id.wrapping_sub(1))) {
        let item = items.get_mut(*token)?;
        if *item == id {
            return Some(item);
        }
    }

    None
}

I expected to see this happen: Code should compile because the latter get_mut calls don't overlap with preceding ones.

Instead, this happened: Got following error

error[E0499]: cannot borrow `*items` as mutable more than once at a time
  --> src/lib.rs:16:20
   |
3  | pub fn find<'a>(lookup: &HashMap<u32, usize>, items: &'a mut Vec<u32>, id: u32) -> Option<&'a mut u32> {
   |             -- lifetime `'a` defined here
...
9  |         let item = items.get_mut(*token)?;
   |                    ----- first mutable borrow occurs here
10 |         if *item == id {
11 |             return Some(item);
   |                    ---------- returning this value requires that `*items` is borrowed for `'a`
...
16 |         let item = items.get_mut(*token)?;
   |                    ^^^^^ second mutable borrow occurs here

It works if I comment the if condition inside the second if let:

use std::collections::HashMap;

pub fn find<'a>(lookup: &HashMap<u32, usize>, items: &'a mut Vec<u32>, id: u32) -> Option<&'a mut u32> {
    if let Some(token) = lookup.get(&id) {
        return items.get_mut(*token);
    }

    if let Some(token) = lookup.get(&id.wrapping_add(1)) {
        let item = items.get_mut(*token)?;
        // if *item == id {
            return Some(item);
        // }
    }

    if let Some(token) = lookup.get(&(id.wrapping_sub(1))) {
        let item = items.get_mut(*token)?;
        if *item == id {
            return Some(item);
        }
    }

    None
}

if I comment the last if let, then also it works:

use std::collections::HashMap;

pub fn find<'a>(lookup: &HashMap<u32, usize>, items: &'a mut Vec<u32>, id: u32) -> Option<&'a mut u32> {
    if let Some(token) = lookup.get(&id) {
        return items.get_mut(*token);
    }

    if let Some(token) = lookup.get(&id.wrapping_add(1)) {
        let item = items.get_mut(*token)?;
        if *item == id {
            return Some(item);
        }
    }

    // if let Some(token) = lookup.get(&(id.wrapping_sub(1))) {
    //     let item = items.get_mut(*token)?;
    //     if *item == id {
    //         return Some(item);
    //     }
    // }

    None
}

So there are still multiple mutable borrows but it works for those cases. But why not in the first case? Is it incorrect borrowing pattern somehow that I am not seeing?

Meta

rustc --version --verbose:

rustc 1.46.0-nightly (346aec9b0 2020-07-11)
binary: rustc
commit-hash: 346aec9b02f3c74f3fce97fd6bda24709d220e49
commit-date: 2020-07-11
host: x86_64-unknown-linux-gnu
release: 1.46.0-nightly
LLVM version: 10.0

Thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.fixed-by-poloniusCompiling with `-Zpolonius` fixes this issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions