为什么在 Result<_, &str> 上使用 '?' 会导致生命周期错误?

3

我有一段无法编译的代码:

impl BytePacketBuffer {
    fn read(&mut self) -> Result<u8, &str> {
        if self.pos >= 512 {
            return Err("End of buffer".into());
        }
        let res = self.buf[self.pos];
        self.pos += 1;

        Ok(res)
    }

    fn read_u16(&mut self) -> Result<u16, &str> {
        let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
        Ok(res)
    }
}

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/byte_packet_buffer.rs:53:51
   |
52 |     fn read_u16(&mut self) -> Result<u16, &str> {
   |                 - let's call the lifetime of this reference `'1`
53 |         let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
   |                     ------------                  ^^^^^^^^^^^ second mutable borrow occurs here
   |                     |
   |                     first mutable borrow occurs here
   |                     returning this value requires that `*self` is borrowed for `'1`

但如果我将返回类型从&str修改为String,它可以编译通过而没有错误。有人能解释一下为什么返回&str会出错,而返回String不会吗?


嗯...一个有趣的案例。我有一种感觉,这是带有?解糖的NLL案例#3。让我们用Polonius测试一下...是的。它在Polonius中可以工作。 - Chayim Friedman
2个回答

4

由于您没有指定任何生命周期,Rust将根据 生命周期省略规则 推断生命周期为:

fn read<'a>(&'a mut self) -> Result<u8, &'a str>

这意味着只要您保留“Result”或其包含的任何内容,它就会推断“self”被借用了一段时间。 为了解决这个问题,您可以简单地指定返回的“str”的生命周期,因为它总是静态的。
fn read(&mut self) -> Result<u8, &'static str>

这“修复”了症状,但并没有解释实际发生了什么。事实上,Rust 过于严格了,这个例子应该是允许的。 - Peter Hall
3
@PeterHall 我为什么没有解释发生了什么?因为省略的生命周期,Rust会推断它们,这是基于简单规则的,会导致错误的假设,通过提供正确的生命周期来修复它。 - cafce25
不行,因为 'static 比这个需要更加严格。它排除了从 self 借用的 &str 是有效的用法。 - Peter Hall

3
@cafce25很好地解释了生命周期省略如何导致生命周期错误,但这仍然没有解释为什么会失败。即使有不正确的生命周期,当我们借用`self`作为`&str`时,它是错误的情况,并且错误情况立即从函数返回,因此不会进一步借用`self`。
嗯,这是因为`?`运算符大致展开成(简化了,但实际展开在这里并不重要):
match expr {
    Ok(v) => v,
    Err(e) => return Err(From::from(e)),
}

而众所周知,借用检查器在分支方面过于保守。它将借用扩展到返回表达式的整个函数中,导致&str即使实际上无法存活也会继续存在。

如果使用Poloniuscargo +nightly rustc -- -Zpolonius)进行编译,则可以成功编译。


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