给定是一个结构,它包含一个包含一些字节代码和指令指针的结构。它实现了获取,解码和执行的模式:
use std::convert::TryFrom;
/// Trait for a virtual machine.
pub struct VirtualMachine {
code: CodeMemory,
instruction_pointer: usize,
}
impl VirtualMachine {
pub fn new(byte_code: Vec<u8>) -> VirtualMachine {
VirtualMachine {
code: CodeMemory::new(byte_code),
instruction_pointer: 0,
}
}
/// Run a given program.
pub fn run(&mut self) -> Result<(), &str> {
loop {
let opcode = self.fetch();
if opcode.is_err() {
return Err(opcode.unwrap_err());
}
let instruction = self.decode(opcode.unwrap());
if instruction.is_err() {
return Err("Bad opcode!");
}
let instruction = instruction.unwrap();
if instruction == Instruction::Halt {
return Ok(());
}
self.execute(instruction);
}
}
fn fetch(&mut self) -> Result<u8, &str> {
self.code.fetch(self.instruction_pointer)
}
fn decode(&mut self, opcode: u8) -> Result<Instruction, Error> {
Instruction::try_from(opcode)
}
fn execute(&mut self, instruction: Instruction) {
self.inc_instruction_pointer();
match instruction {
Instruction::Nop => (),
Instruction::Halt => panic!("The opcode 'halt' should exit the loop before execute!"),
}
}
fn inc_instruction_pointer(&mut self) {
self.instruction_pointer += 1;
}
}
struct CodeMemory {
byte_code: Vec<u8>,
}
impl CodeMemory {
fn new(byte_code: Vec<u8>) -> CodeMemory {
CodeMemory { byte_code }
}
fn fetch(&self, index: usize) -> Result<u8, &str> {
if index < self.byte_code.len() {
Ok(self.byte_code[index])
} else {
Err("Index out of bounds!")
}
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
UnknownInstruction(u8),
UnknownMnemonic(String),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Instruction {
Nop,
// ...
Halt,
}
impl TryFrom<u8> for Instruction {
type Error = Error;
fn try_from(original: u8) -> Result<Self, Self::Error> {
match original {
0x01 => Ok(Instruction::Nop),
0x0c => Ok(Instruction::Halt),
n => Err(Error::UnknownInstruction(n)),
}
}
}
编译器抱怨说:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:20:26
|
18 | pub fn run(&mut self) -> Result<(), &str> {
| - let's call the lifetime of this reference `'1`
19 | loop {
20 | let opcode = self.fetch();
| ^^^^ mutable borrow starts here in previous iteration of loop
...
23 | return Err(opcode.unwrap_err());
| ------------------------ returning this value requires that `*self` is borrowed for `'1`
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:26:31
|
18 | pub fn run(&mut self) -> Result<(), &str> {
| - let's call the lifetime of this reference `'1`
19 | loop {
20 | let opcode = self.fetch();
| ---- first mutable borrow occurs here
...
23 | return Err(opcode.unwrap_err());
| ------------------------ returning this value requires that `*self` is borrowed for `'1`
...
26 | let instruction = self.decode(opcode.unwrap());
| ^^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:38:13
|
18 | pub fn run(&mut self) -> Result<(), &str> {
| - let's call the lifetime of this reference `'1`
19 | loop {
20 | let opcode = self.fetch();
| ---- first mutable borrow occurs here
...
23 | return Err(opcode.unwrap_err());
| ------------------------ returning this value requires that `*self` is borrowed for `'1`
...
38 | self.execute(instruction);
| ^^^^ second mutable borrow occurs here
我想我理解编译器描述的问题,但我找不到解决方案或模式如何以安全的方式在Rust中实现它。是否有可能在循环中改变struct字段?
我正在使用Rust 1.34来使用TryFrom
特征。
答案 0 :(得分:3)
阻止代码示例编译有两件事。
首先,当他们不需要时,你有许多方法被宣布为&mut self
。
VirtualMachine::fetch
只调用CodeMemory::fetch
,不需要自我变异。
VirtualMachine::decode
甚至无法访问VirtualMachine
其次,正如@fintella's answer中所指出的,CodeMemory::fetch
将字符串切片作为错误返回。
您没有指定此字符串切片的生命周期,因此推断它与CodeMemory
实例的生命周期相同,而VirtualMachine
实例的生命周期又与{{1}的生命周期相关联。实例。
这样做的结果是,当你调用fetch
时,不可变借用的生命周期持续到fetch
的返回值的整个范围 - 在这种情况下,几乎整个循环
在这种情况下,您作为错误消息返回的字符串切片是一个字符串文字,它具有静态范围,因此您可以通过将CodeMemory::fetch
的定义更改为:
fn fetch(&self, index: usize) -> Result<u8, &'static str> { /* ... */ }
和VirtualMachine::fetch
:
fn fetch(&self) -> Result<u8, &'static str> { /* ... */ }
进行这些更改后,compiles for me。
答案 1 :(得分:0)
您可能不希望从任何功能返回Result<_, &str>
。如果您使用Result<_, &'static str>
或Result<_, String>
,那么借用核查人员的斗争就会少得多。更好的方法是使用专用的错误类型,但这超出了这个答案的范围。
返回Result<_, &str>
有问题的原因是它最终将返回值的生命周期与self
的生命周期联系起来,这限制了在self
期间使用git check-ignore -v .idea/bbb.txt .idea/workspace.xml .idea/runConfigurations/Android_Debug___ReactNative.xml android/.idea/aaaa.txt android/.idea/runConfigurations/app.xml
的方式结果的一生。