Why my slice gets moved instead of borrowed?

I saw this code:

    ip_repr.emit(frame.payload_mut(), &caps.checksum);
    let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..];
    packet.emit_payload(ip_repr, payload, &caps);

and I'm trying to do the same but for tx_buffer: &mut [u8] as you see below:

    ip_repr.emit(tx_buffer, &caps.checksum);
    let payload = &mut tx_buffer[ip_repr.buffer_len()..];
    packet.emit_payload(ip_repr, payload, &caps);

but I get an error on the let payload = line:

borrow of moved value: `tx_buffer`

value borrowed here after moverustc(E0382)

Since tx_buffer is a mutable reference, then on the emit call, emit borrows it, not move. So I should be able to use it in the second line, borrowing it again. Also, how can it work for frame.payload_mut() without errors, since it's also passed 2 times?

signatures:

    pub fn payload_mut(&mut self) -> &mut [u8]
    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
        &self,
        buffer: T,
        _checksum_caps: &ChecksumCapabilities,
    )
    pub fn emit_payload(&self, _ip_repr: IpRepr, payload: &mut [u8], caps: &DeviceCapabilities)

What is the type of tx_buffer?

&mut [u8] sorry I somehow thought it could be deduced from emit's signature

It can't, e.g. Vec<u8> also implements the traits that emit requires.

Well the thing is that mutable references can be moved. They have exclusive access to their target after all, so it doesn't make sense to duplicated a mutable reference. That said, you can reborrow a mutable reference, which produces a sub-mutable reference.

Reborrowing happens automatically if the method takes a mutable reference, but not when generics are involved. In this case you can explicitly reborrow with

ip_repr.emit(&mut *tx_buffer, &caps.checksum);
3 Likes

Great explanation, is the reborrowing feature documented anywhere? I searched in the reference and nomicon but did not find a related chapter.

I don't know of a chapter off the top of my head, but it's just using the DerefMut impl that works on all mutable references.

impl<'_, T> DerefMut for &'_ mut T where
    T: ?Sized, 

It's similar to how &mut *vec also gives a &mut [u8] through the DerefMut trait.

2 Likes

There's some information on reborrowing in the NLL RFC.

1 Like

thanks, but I have many questions now.

Why it is possible to reborrow?

Is there an exception just for references, or reborrowing is always possible?

Why Rust allows this? Seems unsafe. If something moved it shouldn't be possible to use it again

also why passing frame.payload_mut() two times work? Shouldn't it have moved the payload in the first call?

There was another recent thread on reborrowing that you might find useful to read through. I think some of your questions at least have been answered there.

Nice. They discuss this example:

fn main() {
    let mut x: i32 = 5;
    let y = &mut x;
    // let z = &mut x;
    // ^ fails to compile
    // error[E0499]: cannot borrow `x` as mutable more than once at a time
    let z = &mut *y;
    // compiles fine even though it looks like 
    // we have two simultaneous mutable references against `x`

    *z += 1;
    println!("{}", *z); // '6'
    
    *y += 1;
    println!("{}", *y); // '7'
    
    x += 1;
    println!("{}", x); // '8'
    // ^ change the order to get compilation error 
    // error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
}

I understood their explanation for it:

The output makes it look like mutable references can "stack" where the more recent exclusive access "suspends" the previous one. Once the more recent exclusive access "goes out of use" the previous exclusive access becomes active again.

but it doesn't look that any of the excusive accesses go out of scope before the other is used.

He is saying that z reborrowed so it has exclusive access. He also says that y will have exclusive access once z goes out of scope, but actually y and z go out of scope at the same time, instead of going out of scope in different times.

This is what I didn't understand.

Take a look:

   *z += 1;
    println!("{}", *z); // '6'
    
    *y += 1;
    //z is still alive here, but he was able to use y 
    println!("{}", *y); // '7'
    
    x += 1;
    println!("{}", x); // '8'
    //z still alive here, but he was able to use x

In Rust 1.30 and earlier, borrows ended when the borrowed reference went out of scope. But in current Rust, thanks to non-lexical lifetimes, borrows end after the last use of the reference; it no longer matters where they go out of scope. So z is not considered alive after its last use.

If you modify that example to add another println!("{}", z) at the end of the function, that will extend z's life and prevent use of x and y (Playground).

that explains EVERYTHING!

there's still a minimal problem now:

    ip_repr.emit(tx_buffer, &caps.checksum);
    let payload = &mut tx_buffer[ip_repr.buffer_len()..];
    packet.emit_payload(ip_repr, payload, &caps);

how can I reborrow in this case?

let payload = &mut *tx_buffer[ip_repr.buffer_len()..];

wont work

Just to get ir right, this code worked:

ip_repr.emit(tx_buffer, &caps.checksum);
let payload = &mut tx_buffer[ip_repr.buffer_len()..];
packet.emit_payload(ip_repr, payload, &caps);

because, as in this explanation says: Questions about `&mut T` and move semantics, `&mut T` is "move-only"?, an implicit reborrow occurs?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.