Trying to write a simple virtual machine (bytecode interpreter):

const STACK_SIZE: usize = 1000; mod errors; mod opcodes; mod utils; struct Registers { ... } struct Machine<'a> { registers: Registers, stack: [u64, STACK_SIZE], code: &'a [u8] } impl<'a> Machine<'a> { pub fn new(...) { ... } pub fn fetch(&mut self) -> Result<u8, errors::RuntimeError> { if ... { let next = self.code[self.position()]; ... Ok(next) } else { Err(errors::RuntimeError::EndOfCode) } } pub fn fetch_many(&mut self) -> Result<&[u8], errors::RuntimeError> { if ... { let bytes = &self.code[self.position()..self.position() + n]; ... Ok(bytes) } else { Err(errors::RuntimeError::EndOfCode) } } pub fn position(&self) -> usize { ... } pub fn run(&mut self) { loop { let instruction = self.fetch().expect("unexpected end of code"); match instruction { instruction if instruction == opcodes::Opcode::Push as u8 => { let bytes = self.fetch_many(8).expect("unexpected end of code"); let operand = utils::pack_bytes(bytes); self.stack[self.registers.stack_pointer as usize] = operand; self.registers.stack_pointer -= 1; } _ => {} } } } } 

}

The problem is in the run function. When I try to compile the code, I get the following error message:

 src/main.rs:63:21: 63:80 error: cannot assign to `self.stack[..]` because it is borrowed [E0506] src/main.rs:63 self.stack[self.registers.stack_pointer as usize] = operand; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/main.rs:61:33: 61:37 note: borrow of `self.stack[..]` occurs here src/main.rs:61 let bytes = self.fetch_many(8).expect("end of code"); ^~~~ src/main.rs:63:32: 63:60 error: cannot use `self.registers.stack_pointer` because it was mutably borrowed [E0503] src/main.rs:63 self.stack[self.registers.stack_pointer as usize] = operand; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/main.rs:61:33: 61:37 note: borrow of `*self` occurs here src/main.rs:61 let bytes = self.fetch_many(8).expect("end of code"); ^~~~ src/main.rs:64:21: 64:54 error: cannot assign to `self.registers.stack_pointer` because it is borrowed [E0506] src/main.rs:64 self.registers.stack_pointer -= 1; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/main.rs:61:33: 61:37 note: borrow of `self.registers.stack_pointer` occurs here src/main.rs:61 let bytes = self.fetch_many(8).expect("end of code"); ^~~~ 

I re-read sections of documentation about ownership (ownership) and borrowing (borrowing), but I still didn’t understand what exactly was wrong with me.

  • Does fetch_many record somewhere? Why does he have a mutable link? - D-side
  • @ D-side yes, fetch_many changes self.registers - Daniel Kolesnichenko

1 answer 1

fetch_many returns a link to the slice from self , so as long as this link (stored in the bytes variable) is alive, the object cannot be changed.

As a solution, return from fetch_many not a slice, but a copied value (for example, [u8; 8] ).

Or, in this particular example, you can limit the lifetime of the variable bytes :

 let operand = { let bytes = self.fetch_many(8).expect("unexpected end of code"); utils::pack_bytes(bytes) }; 
  • In my case, returning an array of a previously known length will not work, because the essence of the function fetch_many is just that it returns an arbitrary number of bytes. But the variant with the limitation of the lifetime of the variable bytes worked, thanks. - Daniel Kolesnichenko
  • Cool way. Meanwhile, Rust proposes to make partial borrowing , borrowing of individual fields of the structure. - D-side