How reborrowing works?

Hi everyone !

I faced with an error in my code:

error[E0382]: borrow of moved value: `buffer`
  --> src/client2.rs:26:22
   |
22 |     fn read(buffer: &mut [u8]) -> anyhow::Result<Vec<u8>> {
   |             ------ move occurs because `buffer` has type `&mut [u8]`, which does not implement the `Copy` trait
23 |         let mut reader = std::io::Cursor::new(buffer);
   |                                               ------ value moved here
...
26 |         let packet = buffer[..pos].to_vec();
   |                      ^^^^^^ value borrowed here after move
   |
help: consider creating a fresh reborrow of `buffer` here
   |
23 |         let mut reader = std::io::Cursor::new(&mut *buffer);

and it seems I do not understand the mechanism of the reborrowing. How it works ?
My code example is:

pub trait BytesRead {
    fn read(buffer: &mut [u8]) -> anyhow::Result<Vec<u8>>;
    fn zeroize(buffer: &mut [u8]) {
        buffer.zeroize();
    }
}

#[derive(Default)]
pub struct PacketReader;
impl BytesRead for PacketReader {
    fn read(buffer: &mut [u8]) -> anyhow::Result<Vec<u8>> {
        let mut reader = std::io::Cursor::new(buffer);
        let _ = Incoming::read(&mut reader)?;
        let pos = reader.position() as usize;
        let packet = buffer[..pos].to_vec();
        Self::zeroize(&mut buffer[..pos]);

        Ok(packet)
    }
}

#[derive(BinRead, Debug)]
#[br(little)]
struct Incoming {
    size: u8,
    #[br(count = size)]
    body: Vec<u8>,
}

and according to compiler's hint I replace this line:

let mut reader = std::io::Cursor::new(buffer);

with this:

let mut reader = std::io::Cursor::new(&mut *buffer);

so this fixed the problem.

Could somebody explain how the reborrowing works ?

Is this summary helpful to you?

1 Like

@jofas link might confuse. It is demoing implicit reborrow. Type inference gets in the way in your case so Cursor uses a move.
Alternative to explicit reborrow is adding the type to Cursor; (Only one of the 3 ways shown is needed.)

let mut reader: std::io::Cursor::<&mut [u8]> = std::io::Cursor::<&mut [u8]>::new(buffer as &mut [u8]);
1 Like

The thing to know is that it doesn't work for generic arguments. Cursor takes some type T, so at the call site Rust doesn't realize it needs to reborrow the reference, and moves it instead.

3 Likes

There are a few basic rules you can use to understand this:

  1. Reference assigned to an &mut _ will be reborrowed. Examples:
let mut a = 6;
let b = &mut a;
let c: &mut i32 = b; // equals `&mut *b`, a reborrow of `a`.

// will reborrow as well
fn myfn(x:&mut i32){

}

As said above, it fails in a case like:

fn foo<T>(_: T) {}

because T isn't necessarily a ref type. This means that instead of reborrowing, it is moved (and because it is a mutable ref, that does not implement Copy.)

It is even more confusing with 2 types, but leave that aside for now.

If you give enough info, the compiler can infer that it is type of &mut i32 . Just like jonh did.

A summary can be found here.

There are also these exercises published today in the forum: Subtle borrowing behavior example.

1 Like