Cannot borrow `x` as mutable more than once at a time

In the following code (playground):

struct Node {
    datum: &'static str,
    edges: Vec<Node>,
}

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node {
    node.edges.push(Node {
        datum: data,
        edges: Vec::new(),
    });
    &node.edges[node.edges.len() - 1] // return just added one
}

fn traverse<F>(root: &Node, callback: &F)
where
    F: Fn(&'static str),
{
    callback(root.datum);
    for node in &root.edges {
        traverse(node, callback);
    }
}

fn main() {
    let mut tree = Node {
        datum: "start",
        edges: Vec::new(),
    };

    let lvl1 = add(&mut tree, "level1");

    traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
}

I have this error:

error[E0499]: cannot borrow `tree` as mutable more than once at a time
  --> src/main.rs:32:19
   |
30 |     let lvl1 = add(&mut tree, "level1");
   |                         ---- first mutable borrow occurs here
31 | 
32 |     traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
   |                   ^^^^ second mutable borrow occurs here
33 | }
   | - first borrow ends here

My question seems to be very similar to Why does Rust want to borrow a variable as mutable more than once at a time?, but I'm not sure. If so, is there a workaround for this case?


Solution 1:

This happens because of how add is defined:

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

Here it is specified that the lifetime of the resulting reference should be equal to the lifetime of the incoming reference. The only way it is possible (except for unsafe code) is that the resulting reference is somehow derived from the incoming reference, for example, it references some field inside the object the incoming reference points at:

struct X {
    a: u32,
    b: u32,
}

fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
    &mut x.a
}

However, there is no way for the compiler to know what exactly from the incoming structure is borrowed by looking only at the function signature (which, in general, is the only thing it can do when compiling code which uses this function). Therefore, it can't know that the following code is technically correct:

let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;

We know that a and b are disjoint because they point at different parts of the structure, but the compiler can't know that because there is nothing in borrow_a's signature which would suggest it (and there can't be, Rust does not support it).

Therefore, the only sensible thing the compiler could do is to consider the whole x to be borrowed until the reference returned by borrow_a() is dropped. Otherwise it would be possible to create two mutable references for the same data, which is a violation of Rust aliasing guarantees.

Note that the following code is correct:

let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;

Here the compiler can see that a and b never point to the same data, even though they do point inside of the same structure.

There is no workaround for this, and the only solution would be to restructure the code so it doesn't have such borrowing patterns.

Solution 2:

The behaviour is logical. Consider what

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

means.

This says that &mut Node has a lifetime equal to the lifetime of its return value. Because you assign the return value to a name, it lives until the end of the scope. Thus, the mutable borrow also lives that long.

If you can easily discard the return value, do so. You can just drop it on the floor:

let mut tree = Node {
    datum: "start",
    edges: Vec::new(),
};

add(&mut tree, "level1");

traverse(&mut tree, &|x| println!("{:}", x));

or you can use a lexical scope to constrain it without dropping it completely.

If you want to borrow the return without forcing the mutable borrow to live that long too, you are probably going to have to split the function in two. This is because you are not able to borrow the return value from the mutable borrow to do so.