HiveBrain v1.2.0
Get Started
← Back to all entries
debugrustModerate

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

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
oncethantimemutablecannotborrowmore

Problem

In the following code (playground):

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

fn add(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(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

This happens because of how add is defined:

fn add(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(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.

Code Snippets

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node
struct X {
    a: u32,
    b: u32,
}

fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
    &mut x.a
}
let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;
let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;

Context

Stack Overflow Q#31281155, score: 43

Revisions (0)

No revisions yet.