【问题标题】:Seemingly inconsistent borrow-checker behaviour in loops循环中看似不一致的借用检查器行为
【发布时间】:2021-02-13 02:41:15
【问题描述】:

在测试基本插件系统的实现时,我遇到了一些看似不一致的借用检查器行为。

具有以下结构

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);
}

所以范围界定似乎并不是影响行为变化的因素。

为什么将代码分成两个循环会使代码停止产生错误?

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:

    嗯,结构的生命周期在最后一个使用位置结束。

    详细来说,您的原始代码的错误消息:

    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 仍然存在。

    如果你将systemplugin 操作分成两个“for”循环它不会出错的原因是,在第一个“for”循环之后你不使用system,所以编译器决定system 的生命周期在第一个“for”循环之后立即结束。这样就不会有借用违规了。

    在一个循环中使用作用域没有帮助的原因仍然是,您还有循环的下一次迭代,所以system 的生命周期不会在下一句之前结束。

    【讨论】:

      【解决方案2】:

      当您执行system.attach_plugin(&amp;mut plugin) 时,您已为system 提供了对plugin专有 引用。因此,当system 存在时,不能使用plugin

      编译代码有效,因为编译器发现在第一个循环之后system 不再使用。因此它可以释放对引用的独占保留,允许再次使用 plugin

      【讨论】:

        猜你喜欢
        • 2011-05-11
        • 2014-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多