0

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 :)

codearm
  • 185
  • 1
  • 9
  • Possible duplicate of [Simple refactoring triggers borrow checker error](https://stackoverflow.com/questions/57017747/simple-refactoring-triggers-borrow-checker-error) – hellow Jul 17 '19 at 11:25

1 Answers1

1

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.

Joe Clay
  • 33,401
  • 4
  • 85
  • 85
  • Many thanks Joe! Is it possible somehow to still declare buffer outside the loop but still let the compiler know to release it on every iteration in an idiomatic way? – codearm Jul 17 '19 at 13:51
  • You would need to restructure your code somewhat - either by making `do_something` accept `&mut [IoSliceMut; 2]` or `&[IoSliceMut; 2]` as the parameter, or by using a `RefCell` or a `Mutex` to move the checks to runtime instead of compile time. Fundementally, you cannot create an immutable reference to something while you're still holding on to a mutable reference - that's one of the core guarentees of Rust. – Joe Clay Jul 17 '19 at 14:49
  • I'd also give some thought to whether you actually *need* to pull `buffer` out of the loop - is there significant overhead to creating a new `IoSliceMut` in each iteration, or is it a lightweight wrapper? If you find yourself jumping through borrow checker hoops to re-use them, I'd maybe take a step back and benchmark :) – Joe Clay Jul 17 '19 at 14:52