2021-12-01

Why does the borrow checker raise an error when I keep a reference to an element in a vector that is appended to within a loop?

I have two structs, Holder and Held. Holder holds a reference to Held. Held holds an i32:

struct Holder<'a> {
    val: &'a Held,
}

#[derive(Debug)]
struct Held(i32);

I want to create 10 Holders in a Vec<_> named holders. Since Holder takes a reference to Held struct, I also create a Vec<_> named heldvals that will store the Held structs for the scope of main function:

pub fn main() {
    // contains the `Holder`s
    let mut holders = vec![];

    // contains the `Held`s
    let mut heldvals = vec![];

    for i in 0..10 {
        heldvals.push(Held(i));

        holders.push(Holder {
            val: &heldvals.last().unwrap(),
        });
    }
}

When I attempt to compile this program, I get an error:

error[E0502]: cannot borrow `heldvals` as mutable because it is also borrowed as immutable
   |
   |         heldvals.push(Held(i));
   |         ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
   | 
   |         holders.push(Holder {
   |         ------- immutable borrow later used here
   |             val: &heldvals.last().unwrap(),
   |                   -------- immutable borrow occurs here

I am not able to understand the cause of the error.

As a workaround, I decide to use unsafe reluctantly, which works without any errors. I even implemented the Drop trait to confirm that there is no memory issue.

// ...
impl Drop for Held {
    fn drop(&mut self) {
        dbg!(self);
    }
}

pub fn main() {
    let mut holders = vec![];

    let mut heldvals = vec![];
    let hptr = &mut heldvals as *mut Vec<Held>;

    for i in 0..10 {
        println!("creation");
        unsafe {
            (*hptr).push(Held(i));
        }

        holders.push(Holder {
            val: &heldvals.last().unwrap(),
        });
        println!("replacement");
    }
}

Running the code above gives this output (output reduced):

creation
replacement (10 times)
[src/main.rs:12] self = Held(
    0,
)
... 
[src/main.rs:12] self = Held(
    9,
)

Valgrind shows no memory leaks or issues either:

HEAP SUMMARY:
    in use at exit: 0 bytes in 0 blocks
  total heap usage: 18 allocs, 18 frees, 3,521 bytes allocated

All heap blocks were freed -- no leaks are possible

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Questions

  1. Is there any way to avoid the usage of unsafe?

    1.1. I also found out about Vec::reserve(), is that a good idea?

Edit

Usage of Reference Counters is impossible for me. I want a simple way to hold references until program exits. The linked answer, and other answers of similar kind are too simplistic and do not explain the relation between loops and borrow errors. Moreover, they do not give a pointer towards an alternative solution.



from Recent Questions - Stack Overflow https://ift.tt/3D8lPnF
https://ift.tt/eA8V8J

No comments:

Post a Comment