0

I am going through the "fighting the borrow checker phase" of learning Rust. I have this code:

use std::collections::HashMap;

#[derive(Default)]
struct Foo<'a> {
    str_bar: HashMap<String, Bar<'a>>,
}

struct Bar<'a> {
    x: u32,
    y: Vec<&'a Fiz>,
}

struct Fiz {
    x: i32,
    y: i32,
}

impl<'a> Foo<'a> {
    pub fn new() -> Foo<'a> {
        Foo::default()
    }

    pub fn add_str_bar(&mut self, string: String, fiz: &'a Fiz) {
        self.str_bar.insert(string, Bar { x: 0, y: vec![fiz] });
    }

    pub fn read_str_bar(&mut self, string: String, fiz: &'a mut Fiz) {
        // do something...
    }
}

fn main() {
    let mut my_foo = Foo::new();
    let mut my_bar = Fiz { x: 0, y: 1 };

    // I can't change the order of this
    my_foo.add_str_bar(String::from("foobar"), &my_bar);
    my_foo.read_str_bar(String::from("fizbar"), &mut my_bar);

    println!("hello world");
}

This does not compile:

error[E0502]: cannot borrow `my_bar` as mutable because it is also borrowed as immutable
  --> src/main.rs:38:49
   |
37 |     my_foo.add_str_bar(String::from("foobar"), &my_bar);
   |                                                ------- immutable borrow occurs here
38 |     my_foo.read_str_bar(String::from("fizbar"), &mut my_bar);
   |            ------------                         ^^^^^^^^^^^ mutable borrow occurs here
   |            |
   |            immutable borrow later used by call

How do I get around this without changing the order of the statements?

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
xilpex
  • 2,929
  • 2
  • 11
  • 41
  • When `read_str_bar` runs, `my_foo` holds a reference to `my_bar`, so it is impossible to pass a mutable reference to `my_bar` anywhere. What does `read_str_bar` do in this case? Do you actually need `add_str_bar` to store a reference and not a copy of `my_bar`? – loganfsmyth Dec 09 '20 at 02:57
  • @loganfsmyth -- `read_str_bar` modifies `fiz`, and I do need `add_str_bar` to store a reference. – xilpex Dec 09 '20 at 03:01
  • Then your code is not possible to write with references. A `&mut T` cannot have any other references of any kind at the same time. This is a fundamental part of the [Rules of References](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references). I'd be pretty upset if my **immutable** reference to `my_bar` **mutated**. – Shepmaster Dec 09 '20 at 03:03
  • I think your question is answered by the answers of [Need holistic explanation about Rust's cell and reference counted types](https://stackoverflow.com/q/45674479/155423); [Situations where Cell or RefCell is the best choice](https://stackoverflow.com/q/30831037/155423); [When I can use either Cell or RefCell, which should I choose?](https://stackoverflow.com/q/30275982/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Dec 09 '20 at 03:05
  • TL;DR: you can't use references because your mutability / immutability is not clearly observable at compile time. [The proposed duplicates applied to your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2d8960967196b78872574f619704eba7) – Shepmaster Dec 09 '20 at 03:08
  • 1
    @Shepmaster -- I believe you're right. I've cast my close vote. Thanks! – xilpex Dec 09 '20 at 03:09

0 Answers0