【问题标题】:Cannot borrow as mutable more than once at a time一次不能多次借用可变的
【发布时间】:2023-02-24 01:46:02
【问题描述】:

我正在尝试用 Rust 构建一个 CHIP-8 模拟器来学习这门语言。我目前一直在尝试解决编译器给我的这个错误,我在标题中写了这个错误。

我将描述模拟器的当前结构,然后指出失败的地方。

首先我有 VM 结构定义如下

pub struct VM {
    cpu: CPU,
    memory: Memory
}

然后我有 CPU 结构,它有一个定义为的方法

pub fn execute(&mut self, vm: &mut VM) -> Result<(), &'static str> {
    // ....
}

最后失败的方法是VM::cpu_execute,定义如下

pub fn cpu_execute(&mut self) -> Result<(), &'static str> {
   self.cpu.execute(&mut self)
}

这是它失败的地方。

我理解错误本身,但在这种情况下我真的不知道如何解决它。 代码看起来像这样的原因是 CPU 和其他 VM 模块可以通信:例如,CPU 可以通过执行 vm.memory() / vm.memory_mut() 来访问内存。

我希望问题和代码很清楚。

【问题讨论】:

  • 这个问题的答案可以很自以为是。但是,一般来说,您要寻找的是内部可变性。
  • 是的,所以你不能传递整个 VM,因为它拥有 CPU,你正试图单独借用它。相反,尝试将执行更改为采用 Memory 而不是 VM
  • @PitaJ 那绝对可以工作,但是我还需要通过其他模块,例如计时器、屏幕等。
  • @AhmedMasud 我不确定我知道那是什么,你能给我链接一些我可以阅读的资源吗?
  • 为什么不通过仅有的VM 而不是 self?但最好只传递内存。

标签: rust borrow-checker


【解决方案1】:

方法一:简单方法

这种方法表示传递单独变异的位(例如,如果您的 CPU 正在变异 VM 的内存,只需将其传递给执行):


#[derive(Default, Debug)]
struct CPU;

#[derive(Default, Debug)]
struct Memory { data: Vec<u8> }

#[derive(Default, Debug)]
pub struct VM {
    cpu: CPU,
    memory: Memory
}


impl CPU {
    pub fn execute(&mut self, mem: &mut Memory) -> Result<(), &'static str> {
        println!("Executing");
        mem.data = b"deadbeaf".to_vec();
        Ok(())
    }    
}

impl VM {
    pub fn run(&mut self) {
        self.cpu.execute(&mut self.memory);
        println!("CPU changed memory to {:#?}", self.memory);
    }
}

pub fn main() {
    let mut vm = VM::default();
    vm.run();
}

优点:

非常容易理解和处理

缺点:

有些有限,你必须传递这些东西。

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=54836ccd4d8dc091846a2bb64df2d4f4

方法二:使用 Rc<RefCell>

这在设计上也有些幼稚:

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Default, Debug)]
struct CPU {
    ip: u8, // instruction pointer
    r1: u8, // register 1
    r2: u8, // register 2
}

#[derive(Default, Debug)]
struct Memory { data: Vec<u8> }

#[derive(Default, Debug)]
pub struct VM {
    cpu: Rc<RefCell<CPU>>,
    memory: Rc<RefCell<Memory>>
}


impl CPU {
    pub fn execute(&mut self, vm: &VM) -> Result<(), &'static str> {
        println!("Executing");
        let mut mem = vm.memory.borrow_mut();
        mem.data = b"deadbeaf".to_vec();
        self.ip += 1; // increments instruction pointer. 
        Ok(())
    }    
}

impl VM {
    pub fn run(&self) {
        let mut cpu = self.cpu.borrow_mut();
        cpu.execute(&self).unwrap();
        println!("CPU changed memory to {:#?}", self.memory);
    }
}

pub fn main() {
    let vm = VM::default();
    println!("VM: {:#?}", vm);
    vm.run();
    println!("VM: {:#?}", vm);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=725587bae3d71159fe94530ac8542e68

在这里,我们能够传递 VM,并且资源会根据需要尝试借用适当的可变或不可变资源。

评论

请对我所说的持保留意见(换句话说,不必将我所说的视为“正确”的做事方式)。

我认为您可能应该考虑如何重新设计代码,以便只有一个“协调器”(状态机),而不是说每个组件(例如 CPU)都知道所有其他组件。

【讨论】:

  • Rc 在 2 中完全没有必要,因为你没有与任何东西共享所有权。
【解决方案2】:

您可以仅传递 VM(使其成为关联方法)并通过 VM 访问 CPU:

pub fn execute(vm: &mut VM) -> Result<(), &'static str> {
    // ....
}

pub fn cpu_execute(&mut self) -> Result<(), &'static str> {
   CPU::execute(self)
}

但是带上&amp;mut self,单独传内存可能会更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多