Using mutable references multiple times

Hello, I am new to rust. After fighting the borrow checker for a little while, I decided to get some help. Here is my main function:

fn main() {
    let my_fiz = Fiz {x: 0, y: 1};
    let mut my_foo = Foo::new();
    my_foo.foobar1(0, String::from("foobar"));
    my_foo.foobar2(0);
}

and here are the associated structs:

#[derive(Debug, Default)]
struct Foo<'a> {
    x: HashMap<String, Bar<'a>>,
    y: HashMap<u32, Fiz>,
}

#[derive(Debug, Default)]
struct Bar<'a> {
    x: u32,
    y: HashMap<&'a Fiz, String>,
}

#[derive(Debug, PartialEq, Eq, Hash)]
struct Fiz {
    x: i32,
    y: i32,
}

impl<'a> Foo<'a> {
    pub fn new() -> Foo<'a> {
        let mut abc = Foo::default();
        abc.y.insert(0, Fiz {x: 10, y: 10});
        abc.x.insert(String::from("foobar"), Bar { x: 0, y: HashMap::new() });
        abc
    }
    
    pub fn foobar1(&'a mut self, x: u32, y: String) -> Entry<'_, &'a Fiz, String> {
        let a = self.y.get(&x).unwrap();
        let b = self.x.entry(y).or_insert_with(Bar::default);
        b.y.entry(a)
    }
    
    pub fn foobar2(&mut self, x: u32) {
        // do something here...
    }
}

You can find the entire code on the playground. The problem with the code is that it raises this error:

error[E0499]: cannot borrow `my_foo` as mutable more than once at a time
  --> src/main.rs:45:5
   |
44 |     my_foo.foobar1(0, String::from("foobar"));
   |     ------ first mutable borrow occurs here
45 |     my_foo.foobar2(0);
   |     ^^^^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

I know that I can't use mutable references multiple times, so how do I get around this? Thanks!

This is most likely never going to work out like you might’ve hoped for with those lifetime parameters on the structs. Try to work without them, e.g.:

use std::collections::HashMap;
use std::collections::hash_map::Entry;

#[derive(Debug, Default)]
struct Foo {
    x: HashMap<String, Bar>,
    y: HashMap<u32, Fiz>,
}

#[derive(Debug, Default)]
struct Bar {
    x: u32,
    y: HashMap<Fiz, String>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct Fiz {
    x: i32,
    y: i32,
}

impl Foo {
    pub fn new() -> Foo {
        let mut abc = Foo::default();
        abc.y.insert(0, Fiz {x: 10, y: 10});
        abc.x.insert(String::from("foobar"), Bar { x: 0, y: HashMap::new() });
        abc
    }
    
    pub fn foobar1(&mut self, x: u32, y: String) -> Entry<'_, Fiz, String> {
        let a = self.y[&x];
        let b = self.x.entry(y).or_insert_with(Bar::default);
        b.y.entry(a)
    }
    
    pub fn foobar2(&mut self, x: u32) {
        // do something here...
    }
}

fn main() {
    let my_fiz = Fiz {x: 0, y: 1};
    let mut my_foo = Foo::new();
    my_foo.foobar1(0, String::from("foobar"));
    my_foo.foobar2(0);
}

In the case of Fiz it doesn’t make too much sense to avoid copying that type anyways. In case this type stands for or becomes something way more complex, you can consider working with Rc or Arc to avoid expensive cloning.

@steffahn -- Hi, thanks for the response. The problem with the approach you laid out is that I can't use something like Vec in Fiz because Vec does not implement Copy:

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct Fiz {
    x: i32,
    y: Vec<i32>,
}

playground

@steffahn -- Could you explain how Rc would be used in this case? Sorry, I'm quite new to this :slight_smile:

While you still need to call clone on Rc/Arc, the clone operation is really cheap, so it shouldn't be a problem. Why do you need Copy?

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
struct Fiz {
    x: i32,
    y: Rc<Vec<i32>>
}

If you need mutable access to the vec you can use Rc::make_mut, which will only clone the vec if it is aliased.

@RustyYato -- Hi, thanks for the reply. I tried implementing what you suggested but was unable to. Here is my attempt.

You’ve almost got it. You need to add the .clone() call here so that you’re copying a instead of trying to move it out of self.y:

pub fn foobar1(&mut self, x: u32, y: String) -> Entry<'_, Fiz, String> {
    let a = self.y[&x].clone();
    let b = self.x.entry(y).or_insert_with(Bar::default);
    b.y.entry(a)
}

(Playground)


Or, if you would rather remove x from self.y, you can do this:

pub fn foobar1(&mut self, x: u32, y: String) -> Entry<'_, Fiz, String> {
    let a = self.y.remove(&x).unwrap();
    let b = self.x.entry(y).or_insert_with(Bar::default);
    b.y.entry(a)
}

(Playground)

1 Like

@2e71828 -- Thanks for the answer. The problem is (correct me if I am incorrect), that will clone Fiz, which I am extremely hesitant to do.

Because of the Rc, cloning Fiz is cheap: it will just copy a pointer and increment a reference counter. It won't copy the contents of the Vec<i32>.

3 Likes