Attribute Macro comemo_macros::memoize

source ·
#[memoize]
Expand description

Memoize a function.

This attribute can be applied to free-standing functions as well as methods in inherent and trait impls.

§Kinds of arguments

Memoized functions can take three different kinds of arguments:

  • Hashed: This is the default. These arguments are hashed into a high-quality 128-bit hash, which is used as a cache key.

  • Immutably tracked: The argument is of the form Tracked<T>. These arguments enjoy fine-grained access tracking. This allows cache hits to occur even if the value of T is different than previously as long as the difference isn’t observed.

  • Mutably tracked: The argument is of the form TrackedMut<T>. Through this type, you can safely mutate an argument from within a memoized function. If there is a cache hit, comemo will replay all mutations. Mutable tracked methods can also have return values that are tracked just like immutable methods.

§Restrictions

The following restrictions apply to memoized functions:

  • For the memoization to be correct, the Hash implementations of your arguments must feed all the information they expose to the hasher. Otherwise, memoized results might get reused invalidly.

  • The only obversable impurity memoized functions may exhibit are mutations through TrackedMut<T> arguments. Comemo stops you from using basic mutable arguments, but it cannot determine all sources of impurity, so this is your responsibility.

  • The output of a memoized function must be Send and Sync because it is stored in the global cache.

Furthermore, memoized functions cannot use destructuring patterns in their arguments.

§Example

/// Evaluate a `.calc` script.
#[comemo::memoize]
fn evaluate(script: &str, files: comemo::Tracked<Files>) -> i32 {
    script
        .split('+')
        .map(str::trim)
        .map(|part| match part.strip_prefix("eval ") {
            Some(path) => evaluate(&files.read(path), files),
            None => part.parse::<i32>().unwrap(),
        })
        .sum()
}