I have run into a snag in my Z80 emulator, it's most like a typical beginners error. I'm looking for advice on whether this is a thought error on my side, and how this could be solved the 'Rust way'.
Here's the situation:
I'm having a number of classes that implement different Z80 chips (CPU, PIO, CTC), and there's a trait called Bus (basic idea taken from rustzx), which must be implemented to 'tie the chips together', a typical situation would be: the CPU executes an IN instruction to read the keyboard matrix, and from within the emulation core routine calls a method on Bus which implements what should happen. This Bus trait method then needs to call methods on other chips, which in turn may also need to call other Bus methods.
So the Bus is a generic callback interface which must be implemented differently for each emulated system, and its implementation describes how different chips in an emulated system are wired together.
All methods on those 'chip classes' that need to call out to the Bus take a mutable reference to an object that implements the Bus trait, mutable because I ultimately need to change the state of other chips, or the emulated system, for instance:
struct CPU {
...
}
impl CPU {
pub fn step(&mut self, bus: &mut Bus) {
// fetch and execute one instruction and
// call out into Bus if data must be externally provided
}
}
A 'System' struct owns the different chip objects, and implements the Bus trait, for instance:
struct System {
pub cpu: CPU,
pub pio: PIO,
pub ctc: CTC
}
impl Bus for System {
pub fn cpu_in(&mut self, port: u16) -> u8 {
// read some data from the PIO back into CPU, this
// may in turn call Bus::pio_in()
self.pio.read_data(self, port)
}
pub fn pio_in(&mut self, port: u16) -> u8 {
...read keyboard matrix and return value...
}
}
Now the problem is that I need to pass a mutable reference to System (which implements the trait Bus) down into an object owned by System:
let mut system = System::new();
system.cpu.step(&mut system);
This produces the error "cannot borrow 'system' as mutable more than once at a time", where the "first mutable borrow" is the system.cpu in the same line.
So my first question: is the borrow checker right, if yes why?
And the second question: any ideas how to fix this? The Bus trait implementation needs mutable access to all the chip objects (cpu, pio, ctc), may be I have a mental block, but no matter how many indirection layers I put inbetween it still is like a snake that bites its own tail