0

I need immutable access to one struct field and mutable access to another one, but they have to be stacked on each other. I've had this problem multiple times and I don't know how to fix this.

use std::collections::HashMap;

struct Bar;
impl Bar {
    fn get(&self) -> i32 {
        100
    }
}
struct Foo {
    chars: HashMap<char, i32>,
    b: Bar,
}

impl Foo {
    fn run(&mut self) {
        self.chars.entry('c').or_insert_with(|| self.b.get() * 100);
    }
}

fn main() {
    let mut m = Foo {
        chars: HashMap::new(),
        b: Bar,
    };
    m.run();
}
error[E0502]: cannot borrow `self` as immutable because `self.chars` is also borrowed as mutable
  --> src/main.rs:16:46
   |
16 |         self.chars.entry('c').or_insert_with(|| self.b.get() * 100);
   |         ----------                           ^^ ----              - mutable borrow ends here
   |         |                                    |  |
   |         |                                    |  borrow occurs due to use of `self` in closure
   |         |                                    immutable borrow occurs here
   |         mutable borrow occurs here
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
osamu
  • 983
  • 1
  • 9
  • 13

1 Answers1

6

The problem is that you're trying to borrow self mutably and immutably, as the compiler says. As pointed out by Stefan, the borrow checker can't distinguish access to fields across closure boundaries, so we need to help it a bit by being more explicit about what we want to borrow and pass to the closure.

A way to do this is to take out a reference to self.b and use that inside the or_insert_with():

use std::collections::HashMap;

struct Bar;
impl Bar {
    fn get(&self) -> i32 {
        100
    }
}
struct Foo {
    chars: HashMap<char, i32>,
    b: Bar,
}

impl Foo {
    fn run(&mut self) {
        let b = &self.b;
        self.chars.entry('c').or_insert_with(|| b.get() * 100);
    }
}

fn main() {
    let mut m = Foo {
        chars: HashMap::new(),
        b: Bar,
    };
    m.run();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
vijoc
  • 683
  • 8
  • 17
  • 4
    Maybe also explain what the problem is: "The borrow checker can distinguish access to different fields, but not across function/closure boundaries. If you use `self` in the closure, it will borrow `self`, not just the field you're trying to access in the closure". – Stefan Jan 30 '18 at 07:19
  • @Stefan fair point. Added some context, hopefully it's more clear now. – vijoc Jan 30 '18 at 07:24
  • Well explained. Thank you! – osamu Jan 31 '18 at 01:53