Matching more than once on the same option creates too many mutable references to self

Viewed 38

This is sort of a conceptual question, but this code does not compile:

struct HashHolder{
    pub thing: HashMap<usize, usize>,
}
impl HashHolder {
    fn insert(&mut self, item: &usize) {
        let test = self.thing.get(item).clone(); // immutable borrow occurs here
        for i in 0..10 {
            match test { // immutable borrow later used here
                Some(t) => {
                    //nothing
                },
                None => {
                    self.thing.insert(0, item.clone()); // mutable borrow occurs here
                }
            }
        }
    }
}

It gives "error[E0502]: cannot borrow self.thing as mutable because it is also borrowed as immutable", and highlights the lines shown above.

However, if I take out the for loop, it compiles just fine! It feels to me like this should compile without issue either way: the scope of the match ends within each iteration of the loop, so all the references should be "fresh". The same error can be produced if I remove the loop and just copy-paste the match block a second time.

My understanding of the referencing/borrowing is:

  1. self is immutably borrowed self.thing.get(item). But after the clone(), it should release that borrow?
  2. self is mutably borrowed at self.thing.insert(...); but once insert finishes execution, that borrow should also be released.

So I don't understand why this error occurs. Moreover, the lines the compiler highlights are useless, since those three lines, without the for loop, are completely fine!

Is there anything I'm wrong about in my understanding of what's happening in this code, and what would be a better structure if I want to match multiple times?

Part of the reason for this approach is that in my actual project, the value of test will vary through the loop and can be given a value in certain cases, so the match might behave differently in different iterations. Maybe this isn't the best approach for the behaviour I want, but I'd still like to understand why it won't even compile.

1 Answers

self.thing.get(item) returns a value of type Option<&usize> which holds a reference to self.thing. When you clone this value, you only clone the Option and you get another Option<&usize> which still holds a reference to self.thing. If you want to get an owned value, you need to clone the inner value with Option::cloned (or Option::copied since usize are Copy) which will give you an Option<usize>.

Related