我的代码无法编译,我不明白为什么。这是一个简化的、人为的版本:
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) = ...
,就无法再访问上一次迭代的借用。
也许这是一生的问题?我无法从编译器消息中看出。
我非常确定您遇到了当前借用检查器已知的最大误报。
新的实验性借用检查器称为
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!();
}
})
}
}