I'm trying to build a CHIP-8 emulator in Rust to learn the langauge. I'm currently stuck trying to solve this error the compiler gives me which I wrote in the title.
I will describe the current structure of the emulator and then I will indicate where it fails.
First of all I have VM
struct defined as follows
pub struct VM {
cpu: CPU,
memory: Memory
}
and then I have the CPU
struct which has a method defined as
pub fn execute(&mut self, vm: &mut VM) -> Result<(), &'static str> {
// ....
}
Finally the method that fails is VM::cpu_execute
defined as this
pub fn cpu_execute(&mut self) -> Result<(), &'static str> {
self.cpu.execute(&mut self)
}
This is where it fails.
I understand the error in and of itself, but in this context I really don't know how to fix it.
The reason the code looks like this is so that the CPU and other VM modules can communicate: for example the CPU can access the memory by doing vm.memory() / vm.memory_mut()
.
I hope the question and the code is clear.
In many other languages, you can pass a reference into a child object's method, but in Rust you can only do this without mut
.
There are many ways to solve it, but I think the best way is to understand, that when you have VM = CPU + Mem
, it's a design smell (in general, not only Rust) to call vm.cpu.execute(&mut vm)
, because what if:
impl CPU {
fn execute(&mut self, vm: &mut VM) {
vm.cpu = CPU::new(); // this is valid code
// => if `self` is `vm.cpu` it now breaks `self` reference, so prevent this we are not allowed to borrow `mut` twice
}
}
If you don't want to change your API, you can do this, but you leave your VM is a bad state for a short while, assuming that creating CPU is cheap.
use std::mem::take;
fn main() {
let mut vm = VM::default();
vm.cpu_execute().expect("should work");
}
#[derive(Default)]
pub struct VM {
cpu: CPU,
memory: Memory
}
impl VM {
pub fn cpu_execute(&mut self) -> Result<(), &'static str> {
// take cpu and leave it initialized with default impl
// => in other words self.cpu is useless for a small amount
// of time now.
let mut cpu = take(&mut self.cpu);
let result = cpu.execute(self);
self.cpu = cpu; // put it back
result
}
}
#[derive(Default)]
struct CPU;
impl CPU {
pub fn execute(&mut self, vm: &mut VM) -> Result<(), &'static str> {
// design smell: vm.cpu is useless and != self
Ok(())
}
}
#[derive(Default)]
struct Memory;
I think the best is to get rid of the design smell a go like this:
fn main() {
let mut vm = VM::default();
vm.cpu_execute().expect("should work");
}
#[derive(Default)]
pub struct VM {
cpu: CPU,
memory: Memory
}
impl VM {
pub fn cpu_execute(&mut self) -> Result<(), &'static str> {
self.cpu.execute(&mut self.memory)
}
}
#[derive(Default)]
struct CPU;
impl CPU {
pub fn execute(&mut self, memory: &mut Memory) -> Result<(), &'static str> {
Ok(())
}
}
#[derive(Default)]
struct Memory;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With