Struct Mutating Vec<Box<dyn Trait>>

What would be an idiomatic version of something like this playground:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5aed3c33d7bb0fb285878915ebce73be

trait Thing {
    fn update(&mut self, parent: &ThingHolder) {
        // Make some mutation of `self` 
        // which depends on method(s) from `parent`
    }
}

// Quick sample `Thing` implementation 
struct Usize(usize);
impl Thing for Usize {
    fn update(&mut self, parent: &ThingHolder) {
        self.0 += 1;  // Note a realistic case would depend on `parent`
    }
}

struct ThingHolder {
    things: Vec<Box<dyn Thing>>,
}
impl ThingHolder {
    fn update_things(&mut self) {
        for t in self.things.iter_mut() {
            t.update(&self);
        }
    }
}
  • struct ThingHolder has a vector of Boxes to trait-objects (Vec<Box<dyn Thing>>)
  • It wants to call a (mutating) update method on each Thing, which depends on (immutable) data from its parent ThingHolder
  • Different implementations of Thing will differ in the content of update

As-is, this is a borrow-checker error:

error[E0502]: cannot borrow `self` as immutable because it is also borrowed as mutable
  --> src/lib.rs:23:22
   |
22 |         for t in self.things.iter_mut() {
   |                  ----------------------
   |                  |
   |                  mutable borrow occurs here
   |                  mutable borrow later used here
23 |             t.update(&self);
   |                      ^^^^^ immutable borrow occurs here

error: aborting due to previous error

Primary alternative I can see is instead of mutating, having Thing::update return a new Box<dyn Thing>> and having the loop replace elements in self.things. This is for some scientific-ish code, so its unclear whether I want the penalty of copying each Thing for each update.

Are there good alternatives I'm missing?

Thanks!

That breaks aliasing rules, because t will see itself via both &mut self, as well as a shared borrow of self via update_argument.things[n].

You could fix that by making all references shared (including fn update(&self, arg: &ThingHolder)) and require Thing to use interior mutability.

Or by storing Vec<Arc<Mutex<dyn Thing>>> and using self.things.iter() and t.clone().lock().update() to work around aliasing problem dynamically.

Or instead of passing all of &self, move parts relevant for update to an inner struct and pass only t.update(&self.relevant_bits_only). Then the compiler will see that self.things and self.relevant_bits_only don't overlap, and can be borrowed independently.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.