0

I'm trying to implement a get_or_insert() style method for a binary search tree. I can't seem to find a trick to getting this to work. The following fails.

I've posted a playground example which duplicates the issue in a similar manner.

struct Foo {
    data: Vec<i32>,
}

impl Foo {
    fn new() -> Self {
        Foo { data: vec![42; 5] }
    }

    fn get_mut(&mut self, index: usize) -> Option<&mut i32> {
        if index < self.data.len() {
            Some(&mut self.data[index])
        } else {
            None
        }
    }

    fn get_or_insert(&mut self, index: usize, value: i32) -> &mut i32 { todo!() }
}

fn main() {
    let mut foo = Foo::new();
    let mut val = foo.get_or_insert(100, 88);
    *val = 42;
}
pub fn get_or_insert(&mut self, key: &K, value: V) -> &mut V {
    match self.get_mut(key) {
        Some(value) => value,
        None => {
            self.insert(key.clone(), value);
            self.get_mut(key).unwrap()
        }
    }
}
error[E0499]: cannot borrow `self.data` as mutable more than once at a time
  --> src/main.rs:35:9
   |
29 |     fn get_or_insert(&mut self, index: usize, value: i32) -> &mut i32 {
   |                      - let's call the lifetime of this reference `'1`
30 |         {
31 |             if let Some(value) = self.get_mut(index) {
   |                                  ---- first mutable borrow occurs here
32 |                 return value;
   |                        ----- returning this value requires that `*self` is borrowed for `'1`
...
35 |         self.data.extend_from_slice(vec![value; index].as_slice());
   |         ^^^^^^^^^ second mutable borrow occurs here

error[E0499]: cannot borrow `self.data` as mutable more than once at a time
  --> src/main.rs:36:14
   |
29 |     fn get_or_insert(&mut self, index: usize, value: i32) -> &mut i32 {
   |                      - let's call the lifetime of this reference `'1`
30 |         {
31 |             if let Some(value) = self.get_mut(index) {
   |                                  ---- first mutable borrow occurs here
32 |                 return value;
   |                        ----- returning this value requires that `*self` is borrowed for `'1`
...
36 |         &mut self.data[index]
   |              ^^^^^^^^^ second mutable borrow occurs here

Scoping the first attempt at acquiring the value also fails.

pub fn get_or_insert(&mut self, key: &K, value: V) -> &mut V {
    {
        let ret = self.get_mut(key);
        if ret.is_some() {
            return ret.unwrap();
        }
    }
    self.insert(key.clone(), value);
    self.get_mut(key).unwrap()
}

I've tried several other ways, but can't find something that works. I'm guessing this is impossible at this point. Is there some trick to getting a feature like this to work?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Todd
  • 4,669
  • 1
  • 22
  • 30
  • The way which works for a standard `HashMap` is https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=68b6e3239f78ca7c67f748ca21732fbf, if you have or can add something similar to `entry()`, it should translate. – Alexey Romanov Jul 05 '21 at 19:43
  • Alright @Shepmaster, I've created a playground example. I wish people would give OP's enough time to fix their question before immediately closing them. – Todd Jul 05 '21 at 20:04
  • @Todd The OP has effectively infinite time to read the site rules before posting. However, that’s not the reason that this question was closed; I closed this question because it’s already been answered before. – Shepmaster Jul 05 '21 at 20:12
  • @Shepmaster no need to be condescending, I'll check the other posting, but the title of it won't lead others who have a similar question to mine to it. – Todd Jul 05 '21 at 20:14
  • @Todd that's exactly why duplicates exist, and why they aren't bad things (like so many people think they are)! This question will stay here, forever linking to the existing material, improving SEO, just like [many of these](https://stackoverflow.com/questions/linked/38023871?lq=1). – Shepmaster Jul 05 '21 at 20:16
  • And I’m not attempting to be condescending; if you’d care to point out where I was and how I could have done better, I’d appreciate it. – Shepmaster Jul 05 '21 at 20:22
  • @Shepmaster, I've heard from others that get a similar feeling when posting on this site then being shot down. It's not pleasant. Posters go out on a limb to a degree by revealing the limits of their own knowledge, so one can be sensitive and take it personally when what they thought was a good question wasn't well received, or they get told for the umpteenth time to review the postings material. The standards are somewhat subjective. – Todd Jul 05 '21 at 20:29
  • 1
    How did I shoot you down? I commented how the post needed to be improved (which you followed more or less, thank you) and linked to material explaining that position as well as tips on how to achieve it. Then I answered the question by linking to a previous question and answer that covers the exact same problem. I’m still missing where I was condescending. – Shepmaster Jul 05 '21 at 20:38
  • 1
    Sorry @Shepmaster, I took some of your statements personally and the wrong way. The other posting did answer my question. It's a problem with scoping that may be fixed in future versions. Thank you for the interaction, it's been helpful. – Todd Jul 05 '21 at 20:57
  • No worries, I’m glad that we arrived at an understanding! Trust me when I say that I understand that particular fear in your stomach when posting a question . I hope you continue to enjoy your Rust journey! – Shepmaster Jul 05 '21 at 21:04

0 Answers0