Returning mutable referernces to optional self fields

Hi, I ran into an interesting problem: I have a struct that contains an optional value. I want a function that takes that struct and returns a mutable reference to it's content. If the value is None than I want to initialize it and return a mutable reference.

My first attempt was:

#[derive(Debug)]
struct Test {
    val: Option<usize>
}

impl Test {
    fn get_mut_or_set(&mut self) -> &mut usize {
        match &mut self.val {
            None => self.val.insert(5),
            Some(num) => num,
        }
    }
}

However I got the error:

error[E0499]: cannot borrow `self.val` as mutable more than once at a time
  --> src/main.rs:14:21
   |
12 |       fn get_mut_or_set(&mut self) -> &mut usize {
   |                         - let's call the lifetime of this reference `'1`
13 |           match &mut self.val {
   |           -     ------------- first mutable borrow occurs here
   |  _________|
   | |
14 | |             None => self.val.insert(5),
   | |                     ^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
15 | |             Some(num) => num,
16 | |         }
   | |_________- returning this value requires that `self.val` is borrowed for `'1`

I thought the compiler just thought I had some mutable reference that I'm holding in None, so I tried an early return:

impl Test {
    fn get_mut_or_set(&'a mut self) -> &'a mut usize {
        if let Some(num) = &mut self.val {
            return num;
        }
        self.val.insert(5)
    }
}

but again I got an error:

error[E0499]: cannot borrow `self.val` as mutable more than once at a time
  --> src/main.rs:16:9
   |
12 |     fn get_mut_or_set<'a>(&'a mut self) -> &'a mut usize {
   |                       -- lifetime `'a` defined here
13 |         if let Some(num) = &mut self.val {
   |                            ------------- first mutable borrow occurs here
14 |             return num;
   |                    --- returning this value requires that `self.val` is borrowed for `'a`
15 |         }
16 |         self.val.insert(5)
   |         ^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

And I can't understand why. Shouldn't the compiler figure out with the annotations that the return reference has the same lifetime as the struct. Is there any solution to this?

1 Like

This is a known deficiency in the current borrow checker implementation. When you take a mutable reference and sometimes return it (Some(num) => num), the borrow checker will treat it as if that borrow is always kept alive until the end of the function, ignoring the conditional control flow.

The workaround is to, by one means or another, avoid taking the borrow you might return until you won't need to take any others. In this particular case, you can use the single borrow that is already present:

        match &mut self.val {
            v @ None => v.insert(5),
            Some(num) => num,
        }

Or, simplify down to Option's existing method:

        self.val.get_or_insert(5)
5 Likes

The borrow checker currently assumes that returning a reference means that the borrow has to last for the entire function.

If you only borrow conditionally, it works:

#[derive(Debug)]
struct Test {
    val: Option<usize>
}

impl Test {
    fn get_mut_or_set(&mut self) -> &mut usize {
        match self.val {
            None => self.val.insert(5),
            Some(ref mut num) => num,
        }
    }
}
6 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.