How would I borrow two element inside a loop?

Viewed 49

What's a good way to operate on one element in an array while still having access to the rest of the array? For example, below is some sample code that does not compile:

fn example(&mut self, n: usize) {
   for i in 0..n {
      if Some(x) = &mut self.items[i] {
         // some code modifying x

         // j = some other index dependent on x
   
         if self.items[j].is_some() { // <--------- cannot borrow self.items[j]
            // do something to x
         } else {
            // do something else to x
         }
      }
   }
}

Here j cannot be determined until x is read, and element j in the array is being read only. Would this be a good candidate to use unsafe (I'm also using tokio so I'm not sure if that would cause unintended consequences)?

2 Answers

One way to do it using only safe code is with split_at_mut:

fn example(&mut self, n: usize) {
    for i in 0..n {
        let (head, tail) = self.items.split_at_mut(i);
        let (cur, tail) = tail.split_at_mut(1);
        if Some(x) = &mut cur[0] {
            // some code modifying x

            // j = some other index dependent on x

            let other = if j < i {
                head[j]
            } else if j > i {
                tail[j - i - 1]
            } else {
                panic!("Need to special-case if j may be equal to i")
            };
            if other.is_some() {
                // do something to x
            } else {
                // do something else to x
            }
        }
    }
}

While split_at_mut would be a solution, I think in your case the easiest way with the least amount of special cases is to temporarily release x and re-borrow it after accessing items[j]:

struct X {
    items: Vec<Option<i32>>,
}

impl X {
    fn example(&mut self, n: usize) {
        for i in 0..n {
            if let Some(x) = &mut self.items[i] {
                *x += 1;

                let j = if *x < 5 { 0 } else { 1 };
                let j_is_some = self.items[j].is_some();

                // Reborrow - Unwrap is fine here because we already know
                //            that `items[i]` is `Some`.
                let x = self.items[i].as_mut().unwrap();
                if j_is_some {
                    *x += 10;
                } else {
                    *x -= 10;
                }
            }
        }
    }
}

fn main() {
    let mut x = X { items: vec![Some(0), None, Some(3), Some(7), Some(10)] };
    println!("{:?}", x.items);
    x.example(4);
    println!("{:?}", x.items);
}
[Some(0), None, Some(3), Some(7), Some(10)]
[Some(11), None, Some(14), Some(-2), Some(10)]
Related