0
votes

I am trying to fill a buffer with some data I get from a file, and then read from that buffer to do stuff with that data. All of that iteratively, inside a loop.

If I do this, it compiles perfectly:

use std::fs::File;
use std::io::{BufReader, IoSliceMut, Read};

fn do_something(buffer_0: &[u8; 8], buffer_1: &[u8; 2]) {
    // Do something
}

fn main() {
    let file = File::open("/path/to/file").expect("Error opening file");

    let mut reader = BufReader::new(file);
    let buffer_0 = &mut [0; 8];
    let buffer_1 = &mut [0; 2];

    loop {
        let buffer = &mut [IoSliceMut::new(buffer_0), IoSliceMut::new(buffer_1)];
        reader
            .read_vectored(buffer)
            .expect("Error reading from file");
        do_something(buffer_0, buffer_1);
    }
}

Now, if I declare buffer outside of the loop, like this:

use std::fs::File;
use std::io::{BufReader, IoSliceMut, Read};

fn do_something(buffer_0: &[u8; 8], buffer_1: &[u8; 2]) {
    // Do something
}

fn main() {
    let file = File::open("/path/to/file").expect("Error opening file");

    let mut reader = BufReader::new(file);
    let buffer_0 = &mut [0; 8];
    let buffer_1 = &mut [0; 2];

    let buffer = &mut [IoSliceMut::new(buffer_0), IoSliceMut::new(buffer_1)];

    loop {
        reader
            .read_vectored(buffer)
            .expect("Error reading from file");
        do_something(buffer_0, buffer_1);
    }
}

The following compilation error appears:

error[E0502]: cannot borrow `*buffer_0` as immutable because it is also borrowed as mutable
  --> src/main.rs:21:22
   |
15 |     let buffer = &mut [IoSliceMut::new(buffer_0), IoSliceMut::new(buffer_1)];
   |                                        -------- mutable borrow occurs here
...
19 |             .read_vectored(buffer)
   |                            ------ mutable borrow later used here
20 |             .expect("Error reading from file");
21 |         do_something(buffer_0, buffer_1);
   |                      ^^^^^^^^ immutable borrow occurs here

Same of course for buffer_1.

I don't understand why this code is considered unsafe inside the loop. Also, is it possible to make it safe without moving the buffer declaration?

Rust gurus wisdom is much appreciated :)

1

1 Answers

1
votes

In the first example, each instance of buffer lasts only for one iteration of the loop. Due to non-lexical lifetimes, the compiler can work out that you don't reuse those instances of IoSliceMut after your call to read_vectored, so the mutable borrows that they hold are released, and the code compiles.

In your second example, you're explicitly saying that those two instances of IoSliceMut should be kept for the entire loop, and they should be used for every call to read_vectored - therefore their mutable borrows cannot not possibly be released until the very last iteration.