Recompute cache on all &mut ref fns

Problem:

pub struct InnerObj { ... };
pub struct SomeCache { ... };

impl InnerObj {
  pub fn foo(&mut self, ...);
  pub fn bar(&mut self, ...);
  pub fn cat(&mut self, ...);
  pub fn dog(&mut self, ...);

  pub fn compute_cache(&self) -> SomeCache;
}

Now, I want a situation where every time a &mut self function is called, the Cache gets updated.

====

The current best solution I have so far (this might be the wrong approach) is:

pub struct OuterObj {
  inner: InnerObj,
  cache: SomeCache,
}

impl OuterObj {
  pub fn foo(&mut self, ...) {
    self.inner.foo(...);
    self.cache = self.inner.compute_cache();
  }

  pub fn bar(&mut self, ...) {
    self.inner.bar(...);
    self.cache = self.inner.compute_cache();
  }

  pub fn cat(&mut self, ...) {
    self.inner.cat(...);
    self.cache = self.inner.compute_cache();
  }

  pub fn dog(&mut self, ...) {
    self.inner.dog(...);
    self.cache = self.inner.compute_cache();
  }
}  

As seen, this is redundant. Is there a better solution to this problem? The main points are:

  • there is some InnerObj with a bunch of &mut self functions
  • InnerObj has a function that generates a Cache object
  • I want this Cache object to update whenever a &mut self function is called

You could make it a bit nicer and more explicit by implementing an RAII guard that mutably Derefs to Self and invalidates the cache when it's dropped, then only accessing the instance through these guard values.

Sometimes it makes sense to recompute when reading rather than modifying? Though this has the downside of making reads &mut.

@alice : Reads happen during OpenGL draw frames calls. I definitely want the cache updated during modifications.

@H2CO3 : RAII / guard / Deref sounds really nice. I was thinking about the possibility of having OuterObj return some "RefObject" to InnerObj, where the RefObject, in it's Drop handler, causes the cache to update.

However, I don't know how to implement this "RefObject" that (1) behaves like a &mut, (2) has a Drop handler, and (3) also enforces Rust mut/ref borrow rules.

Any pointers to blog posts / crates?

Maybe something like this

struct Wrapper(Inner);

impl Wrapper {
    fn mutate(&mut self) -> RecalculateOnDrop<'_>{
        RecalculateOnDrop(&mut self.0)
    }
}

impl Deref for Wrapper {
    type Target = Inner;

    fn deref(&self) -> &Inner { &self.0 }
}

struct RecalculateOnDrop<'a>(&'a mut Inner);

impl Deref for RecalculateOnDrop<'_> {
    type Target = Inner;

    fn deref(&self) -> &Inner { &self.0 }
}

impl DerefMut for RecalculateOnDrop<'_> {
    fn deref_mut(&mut self) -> &mut Inner { &mut self.0 }
}

impl Drop for RecalculateOnDrop<'_> {
    fn drop(&mut self) {
        self.0.reacalculate()
    }
}
4 Likes

This is precisely what I needed. Thanks!