上一次迭代中的可变借用,或者可能是终生问题?

问题描述 投票:0回答:1

我的代码无法编译,我不明白为什么。这是一个简化的、人为的版本:

struct Thing<'a> {
    slice: &'a [u8],
}

struct ThingGetter {
    buffer: [u8; 10],
    index: usize,
}

impl ThingGetter {
    pub fn next_thing(&mut self) -> Option<Thing> {
        self.index += 1;
        if self.index == 42 {
            return None;
        } else {
            let thing = Thing {
                slice: &self.buffer[..],
            };
            return Some(thing);
        }
    }
}

struct Wrapper {
    getter: ThingGetter,
}

impl Wrapper {
    pub fn get_thing(&mut self) -> Option<Thing> {
        loop {
            if let Some(thing) = self.getter.next_thing() {
                return Some(thing);
            } else {
                continue;
            }
        }
    }
}

它失败了:

error[E0499]: cannot borrow `self.getter` as mutable more than once at a time
  --> src/thing.rs:31:34
   |
29 |     pub fn get_thing(&mut self) -> Option<Thing> {
   |                      - let's call the lifetime of this reference `'1`
30 |         loop {
31 |             if let Some(thing) = self.getter.next_thing() {
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ `self.getter` was mutably borrowed here in the previous iteration of the loop
32 |                 return Some(thing);
   |                        ----------- returning this value requires that `self.getter` is borrowed for `'1`

我已经在 Stack Overflow 上苦苦寻找了大量有关多个可变借用和生命周期的答案,但我就是没有看到答案。

如果

continue;
行更改为
return None;
则可以正常编译。

我知道

thing
取决于
getter
,它是可变借用的。我不明白为什么这段代码认为
getter
被借用了两次,一次是在上一次迭代中。一旦您再次调用
if let Some(thing) = ...
,就无法再访问上一次迭代的借用。

也许这是一生的问题?我无法从编译器消息中看出。

这是游乐场的链接。

rust lifetime
1个回答
4
投票

我非常确定您遇到了当前借用检查器已知的最大误报。

新的实验性借用检查器称为

polonius
,成功确认了代码的有效性。不过还不稳定。

RUSTFLAGS="-Zpolonius" cargo +nightly build

有很多类似的例子,但都归结为here所描述的问题。

在大多数情况下,可以解决此问题。然而,在极少数情况下,如果没有

unsafe
代码,就不可能正确解决这个问题。幸运的是,
polonius-the-crab
板条箱为这个小
unsafe
代码提供了良好的封装。在它的帮助下,您的问题可以这样解决:

use ::polonius_the_crab::prelude::*;

struct Thing<'a> {
    slice: &'a [u8],
}

struct ThingGetter {
    buffer: [u8; 10],
    index: usize,
}

impl ThingGetter {
    pub fn next_thing(&mut self) -> Option<Thing> {
        self.index += 1;
        if self.index == 42 {
            return None;
        } else {
            let thing = Thing {
                slice: &self.buffer[..],
            };
            return Some(thing);
        }
    }
}

struct Wrapper {
    getter: ThingGetter,
}

impl Wrapper {
    pub fn get_thing(&mut self) -> Option<Thing> {
        let mut this = self;
        polonius_loop!(|this| -> Option<Thing<'polonius>> {
            if let Some(thing) = this.getter.next_thing() {
                polonius_return!(Some(thing));
            } else {
                polonius_continue!();
            }
        })
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.