michie (sounds like Mickey) — an attribute macro that adds memoization to a function.
Table of contents
- Features
- Non-features
- key_expr
- store_type
- store_init
- Store inference and the default store
- Type requirements
- Generic functions
- Functions that take no input
- How it works
- Why must key_expr be provided
- Support and feedback
- Authored by Mobus Operandi
Features
- Supports
- Plain functions
- Generic functions
- Functions in
implblocks - Functions in trait implementation blocks
- Functions that are default trait implementations
- Thread safe
- Expansion depends on only
std - Hygienic
- Supports recursive functions
- Bring your own store
Non-features
- Caching features: this crate does not provide a caching mechanism other than some trivial implementations. It allows you to bring your own.
- "Blazingly fast": this crate aims to provide a simple and easy-to-use means of memoizing a function. If you actually really require micro-optimized memoization then you'd most likely have to implement it yourself.
key_expr
In each invocation a key is obtained.
It is used to query the function's cache store for a possible hit.
An expression that evaluates into a key must be provided via the key_expr argument.
The expression may use bindings from the function's parameters.
In the following example the key_expr is simply the name of the only parameter.
use memoized;
store_type
A store type may be provided via the store_type argument.
The provided type must implement [MemoizationStore].
Implementations of [MemoizationStore] for [BTreeMap] and [HashMap] are provided.
In the following example, [BTreeMap] is provided as the store:
use memoized;
use BTreeMap;
store_init
By default, the store is initialized via [Default::default()].
Different initialization may be provided via an expression to store_init:
use ;
use HashMap;
Store inference and the default store
An omitted store_type may be inferred from a provided store_init.
If both are omitted, the default store_type is [HashMap].
Type requirements
Bounds apply to the key type and the function's return type.
Some are from the general instrumentation and others are via the store type's implementation of [MemoizationStore].
General bounds
The following apply to the key type and to the function's return type:
- [
Sized]: for one, the instrumentation stores the key in aletbinding. 'static: key and return values are owned by a store which is owned by a static.- [
Send] and [Sync]: for parallel access.
Store bounds
Another source of bounds on the key type and the return type is the implementation of [MemoizationStore] for the store type.
By the way, the provided implementation of [MemoizationStore] for the default store type [HashMap] bounds K: Eq + Hash, R: Clone.
Generic functions
Be mindful of the type requirements when using on a generic function:
use memoized;
use Hash;
Functions that take no input
Functions that take no input are good candidates for compile-time evaluation,
which is usually preferred over runtime caching (such as this crate provides).
Nonetheless, some functions cannot be evaluated at compile time.
A reasonable key_expr for a function that takes no input is ():
use memoized;
How it works
The original function expands into something similar to this:
Why must key_expr be provided
The only conceivable default key_expr is the entire input.
For example, for a function signature:
fn f(a: usize, _b: usize) -> usize
the default key_expr would be (a, _b).
Two potential problems: some parameters might not satisfy bounds on the key type.
Also, the resulting key might be a supervalue of the input of the actual calculation.
To explain the latter problem, here is an example:
use memoized;
// pretend that `key_expr` is omitted and that this is the default
f; // expected miss because it's the first invocation
f; // avoidable miss!
If an accurate key_expr = a had been provided, the second execution would have been a hit.
To summarize, key_expr is mandatory in order to encourage proper consideration of it.
Support and feedback
Authored by Mobus Operandi
This crate is a work by Mobus Operandi — a small community for the regular study of Rust in remote mob programming format.