Dan*_*iel 3 rust borrow-checker
我对这里的问题有点困惑,或者更确切地说,为什么它发生在一种情况下而不会发生在另一种情况下。
我正在编写一个 Gameboy 模拟器并实现一个递增寄存器的函数,这就是函数,它工作正常,没有任何错误:
pub fn increment_reg8(
&mut self,
register_name: RegisterName,
register_byte: RegisterByteName,
) -> u64 {
let target_register = &mut self.registers[register_name][register_byte];
let original_value = *target_register;
*target_register += 1;
if *target_register == 0 {
self.set_flag(Flag::Zero);
} else {
self.clear_flag(Flag::Zero);
}
4
}
Run Code Online (Sandbox Code Playgroud)
现在我尝试将 if 语句更改为
self.set_flag_to(Flag::Zero, *target_register == 0);
Run Code Online (Sandbox Code Playgroud)
这几乎等于之前的 if 语句
fn set_flag_to(&mut self, flag: Flag, value: bool) {
if value {
self.set_flag(flag);
} else {
self.clear_flag(flag);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么此更改会产生双重借用错误?
这是借用检查器的一个有趣的极端案例。self被两个东西使用:(set_flag相当于clear_flag)和target_register,它是对 内部数据的可变引用self。让我们看看你的第一个例子。
*target_register += 1;
if *target_register == 0 {
self.set_flag(Flag::Zero);
} else {
self.clear_flag(Flag::Zero);
}
Run Code Online (Sandbox Code Playgroud)
天真的借用检查员会拒绝这一点。target_register具有对 的一部分的可变引用self,并且set_flag/clear_flag需要对 的全部进行可变访问self,因此这是双重借用。
但 Rust 的借用检查器并不幼稚。这是相当聪明的。它看到它target_register再也不会被使用,因此它会自动删除该值以恢复租约self。Rust 只是悄悄地将你的代码转换成这样。
*target_register += 1;
if *target_register == 0 {
drop(target_register);
self.set_flag(Flag::Zero);
} else {
drop(target_register);
self.clear_flag(Flag::Zero);
}
Run Code Online (Sandbox Code Playgroud)
哪里。dropstd::mem::drop
现在是你的第二个例子。
self.set_flag_to(Flag::Zero, *target_register == 0);
Run Code Online (Sandbox Code Playgroud)
同样,set_flag_to想要一个对 的可变引用self,并且target_register已经是对 的可变引用self。Rust 中的参数求值顺序是从左到右,self.method()语法是语法糖。
YourType::set_flag_to(self, Flag::Zero, *target_register == 0);
Run Code Online (Sandbox Code Playgroud)
所以这个self论点得到了评估。这一切都是self可变的。Flag::Zero并不复杂,因此它通过了借用检查器。但现在我们需要target_register再次访问。但我们已经self可变借贷了,所以我们不能这样做。而且由于我们需要在借用target_register 之后self,我们不能像以前那样自动删除任何东西。
如果您更改评估顺序以便target_register在之前进行评估self,它将再次通过。
let is_zero = *target_register == 0;
self.set_flag_to(Flag::Zero, is_zero);
Run Code Online (Sandbox Code Playgroud)
这将通过借用检查器,因为 Rust 会默默地将其转换为
let is_zero = *target_register == 0;
drop(target_register);
self.set_flag_to(Flag::Zero, is_zero);
Run Code Online (Sandbox Code Playgroud)
&mut self当我们需要时我们可以借用最后一行。
| 归档时间: |
|
| 查看次数: |
46 次 |
| 最近记录: |