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 Holder
s 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
As a workaround, I reluctantly decided to use unsafe
, 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 (reduced) output:
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)
Is there a way to avoid the usage of unsafe
? I found out about Vec::reserve()
, is that a good idea?
Cannot borrow as mutable because it is also borrowed as immutable 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.
Usage of reference counters is impossible for me. I want a simple way to hold references until program exits.