I am trying to write a chip8 emulator and the borrow checker is giving me a hard time.
The idea is to decode an opcode through looking up a method pointer inside a HashMap and then executing this method pointer but I cannot get the mutable method pointers to work correctly:
struct Chip8 {
opcode: u16,
//... other fields
jump_table: HashMap<u16, Box<fn(&mut Chip8)>>,
}
Function using the pointers:
fn execute_decoded(&mut self, key: u16) {
let func = self.jump_table.get(&key);
match func {
Some(func) => func(self),
None => {
println!("invalid op: {}", self.opcode);
sleep(Duration::from_millis(10000));
return;
}
}();
self.program_counter = self.program_counter + 2;
}
The checker complains:
cannot borrow `*self` as mutable because `self.jump_table` is also borrowed as immutable
--> main.rs:168:36
|
165 | let func = self.jump_table.get(&key);
| --------------- immutable borrow occurs here
...
168 | Some(func) => func(self),
| ^^^^ mutable borrow occurs here
...
178 | }
| - immutable borrow ends here
I do not understand why this error is happening.
Why is self.jump_table.get(&key) borrowing at all? Based on the signature of execute_decoded, I was assuming that it works on a mutable borrowed version of self and no additional borrowing is needed.
A
HashMapin Rust owns everything inside of it. In order to get your function pointer you are borrowing it withlet func = self.jump_table.get(&key);. So now,funcis immutably borrowingself.jump_table(which is an element ofself).The issue is that you are then trying to pass all of
selfintofunc. This would be fine if you were passing inselfimmutably, as you can borrowselfimmutably as many times as you want. However, since you are trying to mutably borrowselfthe compiler will not allow you to do so since you have just immutably borrowed a portion ofself(specificallyself.jump_table).One way to fix this is to split up your
Chip8struct into smaller structs, such that you can pass all of the necessary information intofuncwithout also passing injump_table.