rustborrow-checkerrefcell

Borrow mutable inside of `match` arm


I'm new to Rust, so I'm sorry if this question has an absolutely obvious solution. I cannot find cleaner way to write the code below. I know this code cause panic because of container is already borrowed.

return match &container.borrow().parent {
  Some(parent) => {
    container.borrow_mut().active = false;
    Layout::move_focus(parent, direction, f)
  }
  None => Rc::clone(container),
}

Problem there is preventing me from changing a container attributes while I'm matching parent. Because of matching parent and changing active state, that are independent in my code, I think this code is safe.

I also know this can be solved by moving mutable borrow out of match and changing match to if let as is shown in the code below. Variable active is set to true at the beginning.

{
  container.borrow_mut().active = false;
}
if let Some(parent) = &container.borrow().parent {
  return Layout::move_focus(parent, direction, f);
}
container.borrow_mut().active = true;
Rc::clone(container)

Is there a more readable and cleaner way to write code with the same functionality?

If it is important for solving this problem the whole code implements nodes that have containers on widgets inside, where containers must know their parents.

Function declaration:

pub fn move_focus(
  container: &Rc<RefCell<Container>>,
  direction: Direction,
  f: fn(&RcCon) -> Option<Rc<RefCell<Container>>>,
) -> Rc<RefCell<Container>>

Structs:

pub struct Container {
    pub items: Vec<Item>,
    pub layout: Layout,
    pub direction: Direction,
    pub parent: Option<Rc<RefCell<Container>>,
    pub act_index: usize,
    pub active: bool,
}

pub enum Item {
    Container(RcCon),
    Widget(Holder),
}

Solution

  • You can borrow whole container as mut firstly, and then do operations on it.

    let mut c = container.borrow_mut();
    
    return match c.parent {
      Some(parent) => {
        c.active = false;
        Layout::move_focus(parent, direction, f)
      }
      None => Rc::clone(container),
    }
    

    Also passing &Rc as an argument is not idiomatic rust. Prefer passing Rc directly.