Recursive function: cannot borrow '.' as mutable more than once at a time

Greetings ! I have a need to traverse a tree structure with N-Child with struct Actor as nodes and struct Transmission as connections.

The requirement is a helper function which takes the root node and a closure called for each node and exposes the current node and the incoming Transmission object as args of the closure.

The previous implementation without mutability was working fine until there was a need for mutations inside the closure.

Is there any work around to achieve the desired needs ?

The implementaion:

pub struct Actor {
    pub name:       String
    pub recipients: Vec<Transmission>
}
pub struct Transmission {
    pub state: i32,
    pub actor: Actor
}

pub struct FlowIterator<'a> { 
    pub callback: Box< dyn FnMut( &mut Actor, Option<&mut Transmission> ) +'a >
} 

impl<'a> FlowIterator<'a> {

    pub fn iterate( root: &mut Actor, callback: impl FnMut( &mut Actor, Option<&mut Transmission>) + 'a ) {
        let mut iter = FlowIterator { callback: Box::new( callback ) };
        iter.iterator( root, None)
    }

    fn iterator( &mut self, node: &mut Actor, incoming_transimission: Option<&mut Transmission >  ) {
        (self.callback)( node, incoming_transimission );
        for recip in &mut node.recipients {
            self.iterator( &mut recip.actor, Some( recip ) );
        }
    }
}

The error:

error[E0499]: cannot borrow `*recip` as mutable more than once at a time
   --> src\documents\flows.rs:381:52
    |
381 |             self.iterator( &mut recip.actor, Some( recip ) );
    |                  --------  ----------------        ^^^^^ second mutable borrow occurs here
    |                  |         |
    |                  |         first mutable borrow occurs here
    |                  first borrow later used by call


Can't you only pass receip here? Why the additional mutable reference to one of its fields when you pass the whole thing anyway?

Trees are hard in Rust and usually require some shared ownership pointer like Rc. Are you familiar with Introduction - Learning Rust With Entirely Too Many Linked Lists?

2 Likes

Another solution is to pass Transmission::state as the 2nd parameter.

Also note that you can skip boxing and pass the callback as a parameter.

Playground with both changes.

Or using Actor methods instead of free functions: playground.

1 Like

Thank you both for your replies.

Another requirement was added which is accessing parent Transmission object (read only) and again the borrow checker was a road block to achieve it.

I had to use the runtime checking to bypass the borrow checker using Ref & RefCell. I also changed the tree structure to begin with Transmission struct instead of Actor.

This implementation work perfectly but does have some down sides:

  • Performance hit
  • Juggling with .borrow(), .borrow_mut() and scopes inside the callback to avoid runtime panics

This implementation:

use std::rc::Rc;
use std::cell::RefCell;

pub type TransmissionRC = Rc< RefCell<Transmission> >;
pub type ActorRC        = Rc< RefCell<Actor> >;

pub struct Transmission {
    pub state: i32,
    pub actor: ActorRC
}

pub struct Actor {
    pub name:       String
    pub recipients: Vec< TransmissionRC >
}

pub struct FlowIterator<'a> { 
    pub callback: Box< dyn FnMut( Option< &TransmissionRC >, &TransmissionRC ) +'a >
} 
impl<'a> FlowIterator<'a> {
    pub fn iterate( root: &TransmissionRC, callback: impl FnMut( Option< &TransmissionRC >, &TransmissionRC ) +'a  ) {
        let iter = &mut FlowIterator{ callback: Box::new( callback ) };
        iter.iterator( None , root );
    }

    pub fn iterator( &mut self, prev_transmission: Option< &TransmissionRC >, curr_tranmsission: &TransmissionRC ) {
        (self.callback)( prev_transmission, curr_tranmsission );       
        
        let curr_trans = &curr_tranmsission.borrow();
        let recepients = &curr_trans.actor.borrow().recipients;
        for receip in recepients {
            self.iterator( Some( curr_tranmsission ), &receip );
        }
    }
}

When I remove the Rc from the latest code you posted, as follows, it still compiles.

pub type TransmissionRC = RefCell<Transmission>;
pub type ActorRC = RefCell<Actor>;

Removing the Rc will avoid an unnecessary allocation.

Playground

Or is the Rc needed for something else?