无法在循环中作为可变借用。

7

我有以下代码

pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
    loop {
        read_exact(buf);

        if let Some(packet) = to_packet(buf) {
            return packet;
        }
    }
}

fn read_exact(_: &mut [u8]) {
    todo!()
}

fn to_packet<'a>(_: &'a [u8]) -> Option<&'a [u8]> {
    todo!()
}

我遇到了如下错误:

error[E0502]: cannot borrow `*buf` as mutable because it is also borrowed as immutable
 --> src/lib.rs:3:9
  |
1 | pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
  |                    -- lifetime `'a` defined here
2 |     loop {
3 |         read_exact(buf);
  |         ^^^^^^^^^^^^^^^ mutable borrow occurs here
4 | 
5 |         if let Some(packet) = to_packet(buf) {
  |                                         --- immutable borrow occurs here
6 |             return packet;
  |                    ------ returning this value requires that `*buf` is borrowed for `'a`

我认为这应该是可行的,因为:

  1. read_exact 中的可变借用在第 3 行完成。
  2. 如果 to_packet 返回 Some,则将该值返回给调用方。
  3. 如果没有,则对 to_packet 的不可变借用在循环结束时结束。因此,在下一次迭代中可以自由地进行可变借用。

有人能告诉我为什么这不起作用吗?

编辑:

这似乎是当前借用检查器的限制。我尝试在夜间使用 Polonius 它可以正常工作。

RUSTFLAGS=-Zpolonius cargo +nightly check
2个回答

2

目前是编译器的限制。 您可以重构为类似以下内容:

pub fn read_packet<'a>(buf: &'a mut [u8]) {
    loop {
        if read_exact(buf) {
            break;
        }
    }
}


fn is_packet(a: &[u8]) -> bool {
    true
}
fn read_exact<'a>(a: &'a mut [u8]) -> bool {
    is_packet(a)
}

fn to_packet<'a>(_: &'a [u8]) -> Option<&'a [u8]> {
    todo!()
}

fn process_packet<'a>(buf: &'a mut [u8]) {
    read_packet(buf);
    let _packet = to_packet(buf);
}

Playground


1
pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] {     |
    let buf = buffer;                                          |
    loop {                                                     |
        read_exact(buf);                                       \/
        if let Some(packet) = to_packet(buf) {              'a _
            return packet;                                     |
        }                                                      |
    }                                                          |
}                                                              
                                         
fn read_exact(_: &mut [u8]) {
    todo!()
}

fn to_packet<'b>(_from: &'b [u8]) -> Option<&'b [u8]> {
    todo!()
}

编译错误:

  |
1 | pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] {     
  |                    -- lifetime `'a` defined here
...
4 |         read_exact(buf);                                       
  |         ^^^^^^^^^^^^^^^ mutable borrow occurs here
5 |         if let Some(packet) = to_packet(buf) {              
  |                                         --- immutable borrow occurs here
6 |             return packet;                                   
  |                    ------ returning this value requires that `*buf` is borrowed for `'a`

使用非词法生命周期(Non Lexical Lifetime,简称NLL):

  • 返回语句约束了packet的生命周期为'a
  • 如果packet'a,那么buf(to_packet)也必须是'a
  • 'a在整个函数中都是有效的。循环会导致共享引用的生命周期与下一次迭代中独占引用的生命周期发生冲突。

即使没有循环,也可以重现这种冲突。这段代码片段由于条件返回而无法编译,原因与buff'a相同。我们可以看到根本原因是条件返回。再次强调,'a必须在整个函数中都有效。

pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
    if let Some(packet) = to_packet(buf) {    'a _
        return packet;                           |
    }                                            |
    read_exact(buf);                             |
    return &[0];                                \/
}

fn read_exact(_: &mut [u8]) {
    todo!()
}

fn to_packet<'b>(_: &'b [u8]) -> Option<&'b [u8]> {
    todo!()
}

编译错误:
  |
1 | pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
  |                    -- lifetime `'a` defined here
2 |     if let Some(packet) = to_packet(buf) {
  |                                     --- immutable borrow occurs here
3 |         return packet;
  |                ------ returning this value requires that `*buf` is borrowed for `'a`
4 |     }
5 |     read_exact(buf);
  |     ^^^^^^^^^^^^^^^ mutable borrow occurs here

使用NLL时,生命周期的推断就像您想象的那样。
pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] {     
    let buf = buffer;                                          
    loop {                                                     
        read_exact(buf);                                       
        if let Some(packet) = to_packet(buf) {              'x _
            return packet;                                     |
        }                                                     \/
    }                                                          
}                                                              
                                         
fn read_exact(_: &mut [u8]) {
    todo!()
}

fn to_packet<'b>(_from: &'b [u8]) -> Option<&'b [u8]> {
    todo!()
}

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