0

I started writing Rust code a few days ago, and just now had my first encounter with the borrow checker.

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Vm<'a> {
    instructions: Rc<InstructionSequence>,
    pc: usize,
    stack: Vec<Value<'a>>,
    frames: Vec<Frame<'a>>,
}

impl<'a> Vm<'a> {
    pub fn run(&'a mut self) {
        loop {
            let instruction = self.instructions.get(self.pc).unwrap();

            match instruction {
                &Instruction::Push(ref value) => {
                    let top_activation = &mut self.frames.last_mut().unwrap().activation;
                    self.stack.push(Vm::literal_to_value(value, top_activation))
                },

                _ => ()
            };
        };
    }
}

full code here

Rust gives me the following errors:

error[E0499]: cannot borrow `self.frames` as mutable more than once at a time
   --> src/vm.rs:157:47
    |
157 |                     let top_activation = &mut self.frames.last_mut().unwrap().activation;
    |                                               ^^^^^^^^^^^
    |                                               |
    |                                               second mutable borrow occurs here
    |                                               first mutable borrow occurs here
...
181 |     }
    |     - first borrow ends here

error[E0499]: cannot borrow `self.frames` as mutable more than once at a time
   --> src/vm.rs:157:47
    |
157 |                     let top_activation = &mut self.frames.last_mut().unwrap().activation;
    |                                               ^^^^^^^^^^^
    |                                               |
    |                                               second mutable borrow occurs here
    |                                               first mutable borrow occurs here
...
181 |     }
    |     - first borrow ends here

error: aborting due to 2 previous errors

I don't understand why it's getting borrowed twice. What's going on?

gmalette
  • 2,439
  • 17
  • 26
  • 3
    We need a [MCVE], because I cannot reproduce your issue without `InstructionSequence`, `Value` and `Frame` and the definition of `literal_to_value`... try to isolate your issue on the Rust Playground and edit your question with a link there. Apart from that, in general you SHOULD NOT have the same lifetime on the `struct` *and* on `&self`. It's a very bad idea, and only occurs when the `struct` tries to store references to its own members... [and that's not possible](http://stackoverflow.com/questions/26349778/how-can-i-provide-a-reference-to-a-struct-that-is-a-sibling). – Matthieu M. Feb 09 '17 at 08:03
  • An `&mut` borrow conflicting with itself is often because the reference is kept alive until the next loop iteration. – Chris Emerson Feb 09 '17 at 09:37

1 Answers1

1

The value you push on the stack keeps a mutable borrow on self.frames active. On the second loop iteration, that borrow is still active, so you can't take a second borrow on self.frames.

Vm::literal_to_value doesn't need a mutable reference to the activation object, so you can change your code to take an immutable reference instead:

match instruction {
    &Instruction::Push(ref value) => {
        let top_activation = &self.frames.last().unwrap().activation;
        self.stack.push(Vm::literal_to_value(value, top_activation))
    },
    _ => ()
};

That makes run compile, but then your tests fail to compile. That's because with this signature:

pub fn run(&'a mut self)

you're linking the lifetime of self with the lifetime parameter on Vm. Essentially, the type of self here is &'a mut Vm<'a>; the fact that 'a occurs twice here, combined with the fact that it's a mutable borrow (rather than an immutable borrow) tells Rust that the Vm maintains a mutable borrow on itself within one of its fields. Therefore, the Vm object will "lock" itself once you call run. That means that after calling run, you can't do anything else on the Vm! The bottom line is that you can't have a field in a struct that is a reference to a value that is owned by the same struct.

Community
  • 1
  • 1
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • This is a good explanation of what's going on, thanks! It also made me realize that my program doesn't allow static lifetimes like I was trying to do. Activation needs to be mutable, so I used an `Rc` – gmalette Feb 11 '17 at 20:35