7

I have the following code:

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!()
}

I get the following error:

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`

I think it should work because:

  1. The mutable borrow in read_exact completes on line 3.
  2. If to_packet returns Some then the value is returned to the caller.
  3. If not, the immutable borrow of to_packet is over at the end of the loop. So it is free to be taken mutable borrow of in the next iteration.

Can somebody please let me know why this doesn't work?

EDIT:

It seems like a current borrow checker limitation. I tried using Polonius in the nightly and it works fine with

RUSTFLAGS=-Zpolonius cargo +nightly check

2 Answers 2

2

It is a compiler limitation atm. You could refactor to something like:

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

Sign up to request clarification or add additional context in comments.

Comments

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!()
}

Compile error:

  |
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`

With Non Lexical Lifetime (NLL):

  • The return statement constrains the packet lifetime to be 'a.
  • If packet is 'a, so buf (to_packet) has to be also 'a.
  • 'a is valid for the entire function. The loop makes the shared reference lifetime conflicts with the exclusive reference lifetime at the next iteration.

The conflict can be reproduced without the loop. This snippet doesn't compile for the same reason that buff is 'a. And we see that the root cause is the conditional return. Again 'a has to be valid for the entire function.

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!()
}

Compile error:

  |
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

With NLL, the lifetime is infered like you thought it would be.

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!()
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.