循环中看似不一致的借用检查器行为
Seemingly inconsistent borrow-checker behaviour in loops
在测试基本插件系统的实现时,我 运行 发现了一些看似不一致的借用检查器行为。
具有以下结构
struct Plugin {
data: [u8; 256],
}
impl Plugin {
fn new() -> Plugin { Plugin { data: [0; 256], } }
fn write(&mut self, port: usize, val: u8) { self.data[port] = val; }
fn read(&self, port: usize) -> u8 { self.data[port] }
}
struct System<'a> {
plugin: Option<&'a mut Plugin>,
}
impl<'a> System<'a> {
fn new() -> System<'a> { System { plugin: None, } }
fn attach_plugin(&mut self, plugin: &'a mut Plugin) { self.plugin = Some(plugin); }
fn detach_plugin(&mut self) { self.plugin = None; }
}
给定以下设置代码
let mut system = System::new();
let mut plugin = Plugin::new();
system.attach_plugin(&mut plugin);
以下代码无效
for i in 0..255 {
system.plugin.as_mut().unwrap().write(i as usize, i);
let val = plugin.data[i as usize]; // This line produces two errors
assert_eq!(val, i);
}
显示这两条错误消息:
error[E0503]: cannot use 'plugin.data' because it was mutably borrowed
和
error[E0503]: cannot use 'plugin.data[_]' because it was mutably borrowed
但是,如果通过系统的访问被分离到它自己的循环中,则代码编译时不会出现错误或警告。
for i in 0..255 {
system.plugin.as_mut().unwrap().write(i as usize, i);
}
for i in 0..255 {
let val = plugin.data[i as usize]; // This line doesn't produce any errors
assert_eq!(val, i);
}
我注意到这两个示例之间唯一显着的区别是,在工作示例中,第一行在其自己的范围内,但对产生错误的代码进行以下更改并不能修复错误:
for i in 0..255 {
{
system.plugin.as_mut().unwrap().write(i as usize, i);
}
let val = plugin.data[i as usize]; // This line still produces two errors
assert_eq!(val, i);
}
因此范围界定似乎并不是影响行为变化的因素。
为什么将代码分成两个循环使代码停止产生错误?
好吧,结构的生命周期在最后使用的地方结束。
详细的,你原代码的错误信息:
error[E0503]: cannot use `plugin.data` because it was mutably borrowed
--> src/main.rs:29:15
|
26 | system.attach_plugin(&mut plugin);
| ----------- borrow of `plugin` occurs here
27 | for i in 0..255 {
28 | system.plugin.as_mut().unwrap().write(i as usize, i);
| ------------- borrow later used here
29 | let val = plugin.data[i as usize]; // This line produces two errors
| ^^^^^^^^^^^^^^^^^^^^^^^ use of borrowed `plugin`
它清楚地解释了 system
包含对 plugin
的可变引用。在 for 循环中,首先使用 system
,然后在下一行中不变地引用 plugin
。 Rust 不允许同时使用可变引用和不可变引用。请注意,您处于循环中,因此下一次迭代仍使用 system
,即当 let val = plugin.data[i as usize]
正在执行时(至少在第一次迭代中),system
仍然存在。
之所以将 system
和 plugin
操作分成两个“for”循环并没有出错,是因为在第一个“for”循环之后您没有使用 system
,因此编译器决定 system
的生命周期在第一个“for”循环后立即结束。这样就不会出现借用违规了。
在一个循环中使用作用域没有帮助的原因是,你还有下一个循环迭代,所以 system
的生命周期不会在下一个句子之前结束。
当您执行 system.attach_plugin(&mut plugin)
时,您已向 system
提供了对 plugin
的 独占 引用。因此 plugin
不能在 system
存在时使用。
编译代码有效,因为编译器发现在第一个循环后不再使用 system
。因此它可以释放对引用的独占保留,从而允许再次使用 plugin
。
在测试基本插件系统的实现时,我 运行 发现了一些看似不一致的借用检查器行为。
具有以下结构
struct Plugin {
data: [u8; 256],
}
impl Plugin {
fn new() -> Plugin { Plugin { data: [0; 256], } }
fn write(&mut self, port: usize, val: u8) { self.data[port] = val; }
fn read(&self, port: usize) -> u8 { self.data[port] }
}
struct System<'a> {
plugin: Option<&'a mut Plugin>,
}
impl<'a> System<'a> {
fn new() -> System<'a> { System { plugin: None, } }
fn attach_plugin(&mut self, plugin: &'a mut Plugin) { self.plugin = Some(plugin); }
fn detach_plugin(&mut self) { self.plugin = None; }
}
给定以下设置代码
let mut system = System::new();
let mut plugin = Plugin::new();
system.attach_plugin(&mut plugin);
以下代码无效
for i in 0..255 {
system.plugin.as_mut().unwrap().write(i as usize, i);
let val = plugin.data[i as usize]; // This line produces two errors
assert_eq!(val, i);
}
显示这两条错误消息:
error[E0503]: cannot use 'plugin.data' because it was mutably borrowed
和
error[E0503]: cannot use 'plugin.data[_]' because it was mutably borrowed
但是,如果通过系统的访问被分离到它自己的循环中,则代码编译时不会出现错误或警告。
for i in 0..255 {
system.plugin.as_mut().unwrap().write(i as usize, i);
}
for i in 0..255 {
let val = plugin.data[i as usize]; // This line doesn't produce any errors
assert_eq!(val, i);
}
我注意到这两个示例之间唯一显着的区别是,在工作示例中,第一行在其自己的范围内,但对产生错误的代码进行以下更改并不能修复错误:
for i in 0..255 {
{
system.plugin.as_mut().unwrap().write(i as usize, i);
}
let val = plugin.data[i as usize]; // This line still produces two errors
assert_eq!(val, i);
}
因此范围界定似乎并不是影响行为变化的因素。
为什么将代码分成两个循环使代码停止产生错误?
好吧,结构的生命周期在最后使用的地方结束。
详细的,你原代码的错误信息:
error[E0503]: cannot use `plugin.data` because it was mutably borrowed
--> src/main.rs:29:15
|
26 | system.attach_plugin(&mut plugin);
| ----------- borrow of `plugin` occurs here
27 | for i in 0..255 {
28 | system.plugin.as_mut().unwrap().write(i as usize, i);
| ------------- borrow later used here
29 | let val = plugin.data[i as usize]; // This line produces two errors
| ^^^^^^^^^^^^^^^^^^^^^^^ use of borrowed `plugin`
它清楚地解释了 system
包含对 plugin
的可变引用。在 for 循环中,首先使用 system
,然后在下一行中不变地引用 plugin
。 Rust 不允许同时使用可变引用和不可变引用。请注意,您处于循环中,因此下一次迭代仍使用 system
,即当 let val = plugin.data[i as usize]
正在执行时(至少在第一次迭代中),system
仍然存在。
之所以将 system
和 plugin
操作分成两个“for”循环并没有出错,是因为在第一个“for”循环之后您没有使用 system
,因此编译器决定 system
的生命周期在第一个“for”循环后立即结束。这样就不会出现借用违规了。
在一个循环中使用作用域没有帮助的原因是,你还有下一个循环迭代,所以 system
的生命周期不会在下一个句子之前结束。
当您执行 system.attach_plugin(&mut plugin)
时,您已向 system
提供了对 plugin
的 独占 引用。因此 plugin
不能在 system
存在时使用。
编译代码有效,因为编译器发现在第一个循环后不再使用 system
。因此它可以释放对引用的独占保留,从而允许再次使用 plugin
。