'Shared ownership inside a struct (mutable_borrow_reservation_conflict warning)

The following code compiles and runs but emits a mutable_borrow_reservation_conflict warning.

My goal is to have a field all_ops owning a set of Op's implementations (readonly) where each op can be referenced in another container in the same struct (and when the main all_ops container is cleared, used_ops access becomes illegal as expected)

Of course, one could use Rc but it causes performance issues.

Do you have an idea to do that properly ? (i.e. a way which will not become an hard error in the (near?) future).

trait Op {
    fn f(&self);
}

struct OpA;


impl Op for OpA {
    fn f(&self) {
        println!("OpA");
    }
}

struct OpB;

impl Op for OpB {
    fn f(&self) {
        println!("OpB");
    }
}

struct Container<'a> {
    all_ops: Vec<Box<dyn Op>>,
    used_ops: Vec<&'a Box<dyn Op>>, // data pointing to data in all_ops field
}

fn main() {
    let v: Vec<Box<dyn Op>> = vec![Box::new(OpA), Box::new(OpB)];

    let mut c = Container { all_ops: v, used_ops: Vec::new() };
    c.used_ops.push(&c.all_ops.get(0).unwrap());
    c.used_ops.push(&c.all_ops.get(1).unwrap());
    c.used_ops.push(&c.all_ops.get(0).unwrap());
    for op in c.used_ops {
        op.f();
    }
    c.all_ops.clear();
    // c.used.first().unwrap().f(); // cannot borrow `c.all` as mutable because it is also borrowed as immutable
}

Rust playground



Solution 1:[1]

If I replace used_ops: Vec<&'a Box<dyn Op>> by used_ops: Vec<&'a dyn Op>, it seems sufficient to fix the warning.

Unfortunately, Container isn't movable even if all references in used_ops are on objects allocated in the heap (and I understand why since there are references to (inner parts of) the object).

trait Op {
    fn f(&self);
}

struct OpA;


impl Op for OpA {
    fn f(&self) {
        println!("OpA");
    }
}

struct OpB;

impl Op for OpB {
    fn f(&self) {
        println!("OpB");
    }
}

struct Container<'a> {
    all_ops: Vec<Box<dyn Op>>,
    used_ops: Vec<&'a dyn Op>, // data pointing to data in all_ops field
}

fn main() {
    let v: Vec<Box<dyn Op>> = vec![Box::new(OpA), Box::new(OpB)];

    let mut c = Container { all_ops: v, used_ops: Vec::new() };
    c.used_ops.push(c.all_ops.get(0).unwrap().as_ref());
    c.used_ops.push(c.all_ops.get(1).unwrap().as_ref());
    c.used_ops.push(c.all_ops.get(0).unwrap().as_ref());
    for op in c.used_ops.iter() {
        op.f();
    }
    // let c2 = c; // cannot move out of `c` because it is borrowed
}

Playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pascal H.