How can I do an insert into a vec<Box<T>> while using &T?

fn main() {
    let mut vec = vec![Box::new(0),Box::new(1),Box::new(2)];
    let two = vec[2].as_ref();
    vec.insert(0, Box::new(114514));
    println!("two{}",two)
}

This is the compile error:

error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
  --> src\main.rs:25:5
   |
24 |     let two = vec[2].as_ref();
   |               --- immutable borrow occurs here
25 |     vec.insert(0, Box::new(100));
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
26 |     println!("{}",two)
   |                   --- immutable borrow later used here

I know this can be done with Vec<Rc>, but this requires dynamically calculating the number of references.

This will not make the reference a dangling pointer unless &T is used outside the lifetime of vec.

...unless insert triggers reallocation, invalidating all previously created pointers.

1 Like

To be fair, OP used another indirection so reallocation of Vec does not matter.

1 Like

I asked ChatGPT this question, and this is his answer:
When a Vec reallocates, the Box instances stored in the vector will be moved to the new memory location. However, the data owned by the Box (on the heap) remains unchanged.

Rust won't allow that on principle. Definition of insert is unable to express the subtle difference between reference to elements and reference to their heap.

vec.clear() uses the same &mut self in function definition, but it does run destructors and it will invalidate even indirect references to the heap, so this combination of reference and lifetimes can be really unsafe, and that's why the borrow checker won't allow it.

You can use Rc/Arc instead of Box and then you will be able to keep a cloned arc while mutating its storage.

You can also use a memory pool or arena (search for one on https://lib.rs). Arenas will define their lifetimes differently and allow inserts while there are references to their elements.

4 Likes

At least so far, moving of a Box<_> invalidates all borrows through it, so it does matter.

3 Likes