rustborrow-checkerrcrefcell

Mutating fields of Rc Refcell depending on its other internal fields


I need to to iterate over a field of a struct inside Rc RefCell and modify some of its arguments according to its other field. For example for the struct Foo:

pub struct Foo {
    pub foo1: Vec<bool>,
    pub foo2: Vec<i32>,
}

The following code panics:

fn main() {
    let foo_cell = Rc::new(RefCell::new(Foo { foo1: vec![true, false], foo2: vec![1, 2] }));
    foo_cell.borrow_mut().foo2.iter_mut().enumerate().for_each(|(idx, foo2)| {
        if foo_cell.borrow().foo1[idx] {
            *foo2 *= -1;
        }
    });
}

I can solve it by cloning foo1 before the mutable borrow and just using the clone inside the closure but that leads to unnecessary copying. In fact I basically need one mutable reference to foo2 and one immutable reference to foo1 (Please note this is simplified code and zipping the iterators won't do here). I understand the code doesn't comply with borrowing rules.. Is there a way to get this to work without cloning data?


Solution

  • The problem is that you are trying to reborrow the RefCell on each loop iteration while it has already been borrowed to get the iterator over foo2. The solution for this is to use a single foo_cell.borrow_mut() before the loop and get separate borrows to foo1 and foo2.

    Note, the dereference on foo_cell.borrow_mut() is required to make this work.

    use std::rc::Rc;
    use std::cell::RefCell;
    
    pub struct Foo {
        pub foo1: Vec<bool>,
        pub foo2: Vec<i32>,
    }
    fn main() {
        let foo_cell = Rc::new(RefCell::new(Foo {
            foo1: vec![true, false],
            foo2: vec![1, 2]
            
        }));
        // The dereference is required to get &mut Foo out of the RefMut<Foo>
        let borrow = &mut *foo_cell.borrow_mut();
        let foo1 = &borrow.foo1;
    
        borrow.foo2.iter_mut().enumerate().for_each(|(idx, foo2)| {
            if foo1[idx] {
                *foo2 *= -1;
            }
        });
    }
    

    Playground