借用检查器抱怨多次借用,但只会发生一次借用

3
请考虑以下代码(也可在playground上查看):
pub trait TextStream {
    fn next_str(&mut self) -> Option<&str>;
}

pub struct FilterTextStream<T: TextStream> {
    inner: T,
    maxlen: usize,
}

impl<T: TextStream> TextStream for FilterTextStream<T> {
    fn next_str(&mut self) -> Option<&str> {
        match self.inner.next_str() {
            None => None,
            Some(txt) if txt.len() <= self.maxlen => {
                Some(txt) // <-- this does not compile
                //Some(unsafe {&*(txt as *const str)}) // <-- this fixes the problem
            }
            _ => self.next_str()
        }
    }
}
TextStream 特性类似于迭代器,但其产生的 &str 不具有重叠的生命周期。而 FilterTextStream 则类似于(专门化的)过滤器迭代器。

然而,该代码无法编译,出现以下错误:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:18:18
   |
11 |     fn next_str(&mut self) -> Option<&str> {
   |                 - let's call the lifetime of this reference `'1`
12 |         match self.inner.next_str() {
   |               ---------- first mutable borrow occurs here
...
15 |                 Some(txt)
   |                 --------- returning this value requires that `self.inner` is borrowed for `'1`
...
18 |             _ => self.next_str()
   |                  ^^^^ second mutable borrow occurs here

我感到惊讶的是,我并不清楚这里是如何两次借用 self。我们要么在第15行返回,要么在第18行返回,只会发生其中之一。我的假设是,这不是一个真正的问题,而是一个借用检查器无法智能处理的情况。我有什么遗漏的吗?
假设我是正确的,我通过使用一个 unsafe 块(请参见注释掉的第16行)来绕过这个错误,人为地 "破坏" 生命周期依赖关系。有没有更好的方法(即没有任何 unsafe 块)可以使这段代码编译?

1
我认为这是借用检查器当前实现的一个已知问题。不幸的是,我不知道在这种情况下的正确解决方法。 - kmdreko
@kmdreko,感谢您的评论。然而,据我所知,这个RFC已经在稳定版Rust中与版本1.36合并了。因此,您链接中描述的问题可能已经得到解决。但是,可以肯定的是,它与我遇到的问题相似... - Pierre-Antoine
@Pierre-Antoine 我已经提交了错误报告 https://github.com/rust-lang/rust/issues/84361 - Inline
1个回答

1
根据The Polonius Talk,rustc目前将函数签名中的生命周期视为在整个函数体中都有效。因此,如果我理解正确,rustc不关心借用是否在不同的匹配分支上。
使用cargo +nightly rustc -- -Zpolonius进行类型检查,代码可以通过,这意味着当Polonius功能完全集成后,代码应该可以在不进行任何更改的情况下通过类型检查。
我找不到比使用unsafe更好的解决方法,就像在问题中的示例中一样,并添加一个注释说可以在某个时刻删除unsafe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接