I'm encountering a borrow checker issue in Rust where I am attempting to modify a Vec<u8> within a loop using splice, and then obtain a reference to the same Vec immediately afterwards. The error only occurs within the loop, as removing the loop resolves the issue. Below is the minimal code snippet that reproduces the error:
pub struct bz_stream_DState<'a> {
pub next_in: Option<&'a [u8]>,
pub state: *mut DState<'a>,
}
impl<'a> bz_stream_DState<'a> {
pub fn set_next_in(&mut self, buf: &'a [u8]) {
self.next_in = Some(buf);
}
}
pub struct bzFile<'a> {
pub buf_vec: Vec<u8>,
pub strmD: bz_stream_DState<'a>,
}
pub struct DState<'a> {
pub strm: *mut bz_stream_DState<'a>,
}
fn main() {
let mut bzf = bzFile {
buf_vec: vec![1, 2, 3, 4, 5],
strmD: bz_stream_DState {
next_in: None,
state: std::ptr::null_mut(),
},
};
for _i in 1..6 {
let range = 0..1;
let new_elements = [10, 11, 12];
bzf.buf_vec.splice(range, new_elements.iter().cloned());
let temp_buf_vec_ref = &bzf.buf_vec;
bzf.strmD.set_next_in(temp_buf_vec_ref);
}
}
The error message I receive is: (seen in rust playground)
error[E0502]: cannot borrow `bzf.buf_vec` as mutable because it is also borrowed as immutable
--> src/main.rs:35:9
|
35 | bzf.buf_vec.splice(range, new_elements.iter().cloned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
39 | let temp_buf_vec_ref = &bzf.buf_vec;
| ------------
| |
| immutable borrow occurs here
| immutable borrow later used here
The key point to note is that if I remove the for loop, the error does not occur. I've tried to separate the mutable and immutable borrows by introducing a new block scope within the loop, but it hasn't resolved the issue. Could someone help explain why the borrow checker is still not satisfied within the loop context and how to fix this error?
Additionally, if there are any improvements that could be made to the code structure to avoid such borrow checker issues, especially within loops, I would appreciate suggestions on that as well.
Context Background
I am trying to rewrite the original C code of bzip2 into Rust, notice the modification of bzf in the lines
Notice C code within while loop
It first read
n = fread ( bzf->buf, sizeof(UChar),
BZ_MAX_UNUSED, bzf->handle );
Then we translated to Rust code to
match handle.read(&mut bzf.buf_vec[total_read..]) {
using library below
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
Then we use pub next_in: Option<&'a [u8]> to imitate the behavior of the array pointer in C
Vec::splice()returns an iterator, andbzf.buf_vec.splice(..);doesn't actually use that iterator at all. So the code as written degenerates to removingrangefrom thebuf_vec.bzfwhile you hold a shared reference to it, the first loop stores a reference tobzfinbzf, afterwards you can't modify it no more, the loop is irrelevant, doing the things you're trying to do twice is What you're trying to do is very unidiomatic, as storing a reference in the value itself is highly problematic*mut u8in Rust, for obvious reasons we try to avoid it though.moveto move the ownership alternatively between mutable reference and shared reference?