Strange borrow checker behavior when returning content of Option

Let we have the code:

use std::collections::HashMap;

struct Fibonaccer {
   cache: HashMap<isize, isize>,
}

impl Fibonaccer {
  fn get(&mut self, i: isize)-> &isize {
    // if let Some(result) = self.cache.get(&i) {
    //  return result;
    // }

    // let get_result = self.cache.get(&i);
    // if get_result.is_some() {
    //   return get_result.unwrap();
    // }
    
    if self.cache.get(&i).is_some() {
      return self.cache.get(&i).unwrap();
    }

    let mut value = 1;
    if i > 2 {
      value = *self.get(i - 1) + *self.get(i - 2);
    }

    self.cache.entry(i).or_insert(value)
  }
}

Here we add value to cache and return reference to cached value. You can say that I can copy result, because it has tiny type, but in common case it can have huge type.

As you can see, I use very strange illogical way to return cached value. Both commented variants are not accepted by borrow checker:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src\main.rs:24:16
   |
8  |   fn get(&mut self, i: isize)-> &isize {
   |          - let's call the lifetime of this reference `'1`
9  |     if let Some(result) = &self.cache.get(&i) {
   |                            ------------------ immutable borrow occurs here
10 |      return result;
   |             ------ returning this value requires that `self.cache` is borrowed for `'1`
...
24 |       value = *self.get(i - 1) + *self.get(i - 2);
   |                ^^^^^^^^^^^^^^^ mutable borrow occurs here

And... I dont understang, why first or second variant are worse than trird.

It's a short coming in the current generation borrow checker.

2 Likes

Thanks a lot, I will wait for the fix

Might be awhile...

Maybe that was just minimized example, but returning by value would also fix this case.