Attribute Macro comemo_macros::track

source ·
#[track]
Expand description

Make a type trackable.

This attribute can be applied to an inherent implementation block or trait definition. It implements the Track trait for the type or trait object.

§Tracking immutably and mutably

This allows you to

  • call track() on that type, producing a Tracked<T> container. Used as an argument to a memoized function, these containers enjoy fine-grained access tracking instead of blunt hashing.

  • call track_mut() on that type, producing a TrackedMut<T>. For mutable arguments, tracking is the only option, so that comemo can replay the side effects when there is a cache hit.

If you attempt to track any mutable methods, your type must implement Clone so that comemo can roll back attempted mutations which did not result in a cache hit.

§Restrictions

Tracked impl blocks or traits may not be generic and may only contain methods. Just like with memoized functions, certain restrictions apply to tracked methods:

  • The only obversable impurity tracked methods may exhibit are mutations through &mut self. Comemo stops you from using basic mutable arguments and return values, but it cannot determine all sources of impurity, so this is your responsibility. Tracked methods also must not return mutable references or other types which allow untracked mutation. You are allowed to use interior mutability if it is not observable (even in immutable methods, as long as they stay idempotent).

  • The return values of tracked methods must implement Hash and must feed all the information they expose to the hasher. Otherwise, memoized results might get reused invalidly.

  • The arguments to a tracked method must be Send and Sync because they are stored in the global cache.

Furthermore:

  • Tracked methods cannot be generic.
  • They cannot be unsafe, async or const.
  • They must take an &self or &mut self parameter.
  • Their arguments must implement ToOwned.
  • Their return values must implement Hash.
  • They cannot use destructuring patterns in their arguments.

§Example

/// File storage.
struct Files(HashMap<PathBuf, String>);

#[comemo::track]
impl Files {
    /// Load a file from storage.
    fn read(&self, path: &str) -> String {
        self.0.get(Path::new(path)).cloned().unwrap_or_default()
    }
}

impl Files {
    /// Write a file to storage.
    fn write(&mut self, path: &str, text: &str) {
        self.0.insert(path.into(), text.into());
    }
}