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

b0zn9rqh  于 2023-03-30  发布在  其他
关注(0)|答案(1)|浏览(104)

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

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依赖于gettergetter是可变借用的。我不明白的是为什么这段代码认为getter被借用了两次,一次是在上一次迭代中。一旦再次调用if let Some(thing) = ...,上一次迭代中的借用就不再可访问了。
或者这可能是一个终身问题?我不能告诉从编译器消息。
Here's a link to the playground.

wwtsj6pe

wwtsj6pe1#

我很确定你遇到了最大的已知假阳性,目前的借用检查。
新的实验性借位检查器polonius成功地确认了代码的有效性,尽管它还不稳定。

RUSTFLAGS="-Zpolonius" cargo +nightly build

有很多类似的例子,但都归结为here所描述的问题。
在大多数情况下,可以解决这个问题。然而,在少数情况下,如果没有unsafe代码,就不可能正确解决这个问题。幸运的是,polonius-the-crab crate为这个小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!();
            }
        })
    }
}

相关问题