Handling multiple mutable references in a data structure

Hi,

I’m new to Rust and having problems translating some existing code (in Python) to Rust. The data structure I have keeps some running statistics on time series.

This works fine. However I also want to keep some statistics relating to the covariance etc. between two statistics. This is implemented by added a new covariance stat struct and keeping a reference to a vector of these for each stat:

struct Stat {
  id: String,
  ...
  cur_mean: f64,
  cur_variance: f64,
  cov: Vec<StatCov>,
}

struct StatCov {
  ...
  cur_mean: f64,
  cur_variance: f64,
}

The idea is that when a statistic is updated we also update the covariate statistics:

impl Stat {

  fn update(&mut self, time: f64, val: f64) {
    for cov in self.covs {
      cov.update(time, val);
    }
  }
}

The problem happens because we want to have a reference to the StatCov object in both the statistics, so when either of the related statistics is updated the covariate statistics are updated too.

I can’t have two references to the same object or I will fall foul of the borrow checker. I tried making the vector a Vec<Rc> which compiles but when I call Rc::get_mut() in order to update the StatCov object it returns None (which I think is expected?). I’ll probably try something with RefCell next and see if that works but any pointers on how best to handle this would be much appreciated.

Thanks!

If StatCov containts just a bunch of Copy types, like f64, then you can use a Cell instead of a RefCell. A setup like this could work.

Alternatively, you can keep the StatCov in a side table/map, and track which pair of Stat instances impact a given covariance stat. Then whatever code updates individual Stat instances will be in charge of locating the cov stat, and updating it as well.

Thanks, I’ll look into the cell approach. I tried the side HashMap approach already and the performance was poor (overall slower than Python following references). I’m not good at profiling Rust yet but perf told me it was spending substantial time in hash related functions e.g. SipHash.

You likely don’t need to use the siphasher, which is the default used by HashMap - take a look at https://crates.io/crates/fnv for a faster hasher.

Relatedly, you were likely hashing the name (string) of the stat? If the names are long, consider assigning an integral id to the created stats, and then using that id as a handle to them (e.g. if all stats are stored in a Vec, then the id can be just the direct index in the vec of that stat). Then hash on those ids.

Also, make sure you’re testing performance of a release (optimized) build.