3
votes

I am implementing an in-place recursive parser in rust and I am getting some borrow errors. The code snippet reproduces the problem although it is not very useful

 use std::vec::Vec;

struct MyBorrows<'a> {
    val : &'a mut i32
}

impl <'a> MyBorrows<'a> {
    fn new(v : &'a mut i32) -> MyBorrows<'a> {
        MyBorrows { val : v }
    }
}

fn main() {
    let mut my_val = 23;
    let mut my_vec : Vec<Box<MyBorrows>> = Vec::new();
    my_vec.push(Box::new(MyBorrows::new(&mut my_val)));
    for i in [1..4].iter() {
        let mut last : &mut Box<MyBorrows> = my_vec.last_mut().unwrap();
        let mut new_borrow = Box::new(MyBorrows::new(last.val));
        my_vec.push(new_borrow);        
    }
}

This gives me the following error:

error[E0499]: cannot borrow `my_vec` as mutable more than once at a time
  --> test.rs:20:9
   |
18 |         let mut last : &mut Box = my_vec.last_mut().unwrap();
   |                                              ------ first mutable borrow occurs here
19 |         let mut new_borrow = Box::new(MyBorrows::new(last.val));
20 |         my_vec.push(new_borrow);
   |         ^^^^^^ second mutable borrow occurs here
21 |     }
22 | }
   | - first borrow ends here

error: aborting due to 3 previous errors

In my real case, the vector is used as a stack to reference deeper and deeper components of the struct I am parsing into. This is common pattern I use for general purpose parsing in C++ which I am trying to replicate in Rust but I am having problems. Any help will be appreciated.

1

1 Answers

3
votes

What you are trying to do is unsound. It looks like you are attempting to create multiple MyBorrows which all mutably borrow the same value, and have them all alive at once (in the vector). Such setups are exactly what Rust is designed to prevent, as that's how data races occur.

What you might instead want to do is immutably borrow the value a bunch, which is legal. So after cleaning up the unnecessary mutable borrows, I've reduced the problem to:

struct MyBorrows<'a> {
    val : &'a i32
}

impl <'a> MyBorrows<'a> {
    fn new(v : &'a i32) -> MyBorrows<'a> {
        MyBorrows { val : v }
    }
}

fn main() {
    let my_val = 23;
    let mut my_vec = vec![];
    my_vec.push(Box::new(MyBorrows::new(&my_val)));
    for _ in 1..4 {
        let last = my_vec.last().unwrap();
        let new_borrow = Box::new(MyBorrows::new(last.val));
        my_vec.push(new_borrow);
    }
}

You get a slightly different error now:

error[E0502]: cannot borrow `my_vec` as mutable because it is also borrowed as immutable
  --> test.rs:18:9
   |
16 |         let last = my_vec.last().unwrap();
   |                    ------ immutable borrow occurs here
17 |         let new_borrow = Box::new(MyBorrows::new(last.val));
18 |         my_vec.push(new_borrow);
   |         ^^^^^^ mutable borrow occurs here
19 |     }
   |     - immutable borrow ends here

error: aborting due to previous error

This one is trickier, and you have to realize what is going on when you call my_vec.last() -- it's returning a reference to existing memory in the Vec, precluding anything else from touching the Vec. Currently in Rust, this reference lives until the end of the current block. To get around this, encase the mutable borrow in its own block scope:

fn main() {
    let my_val = 23;
    let mut my_vec = vec![];
    my_vec.push(Box::new(MyBorrows::new(&my_val)));
    for _ in 1..4 {
        let new_borrow;
        {
            let last = my_vec.last().unwrap();
            new_borrow = Box::new(MyBorrows::new(last.val));
        }
        my_vec.push(new_borrow);
    }
}

Now the mutable borrow ends before the push occurs, and the lifetimes work. Hopefully in the future, we will get non-lexical lifetimes added to the language, so the compiler can figure out that my first example is actually safe.