{"id":30622471,"url":"https://github.com/mightyiam/michie","last_synced_at":"2026-03-05T17:42:12.605Z","repository":{"id":37042989,"uuid":"470648290","full_name":"mightyiam/michie","owner":"mightyiam","description":"A Rust attribute macro that adds memoization to a function (rhymes with Mickey)","archived":false,"fork":false,"pushed_at":"2023-08-25T17:17:12.000Z","size":362,"stargazers_count":21,"open_issues_count":6,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2026-01-14T11:35:03.068Z","etag":null,"topics":["crate","memoization","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mightyiam.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-16T15:44:59.000Z","updated_at":"2025-06-28T10:15:06.000Z","dependencies_parsed_at":"2024-11-11T15:48:49.375Z","dependency_job_id":null,"html_url":"https://github.com/mightyiam/michie","commit_stats":{"total_commits":205,"total_committers":10,"mean_commits":20.5,"dds":"0.37073170731707317","last_synced_commit":"3f9b8ac0c4786e2c829b345f501c15575520d044"},"previous_names":["mobusoperandi/michie"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/mightyiam/michie","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mightyiam%2Fmichie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mightyiam%2Fmichie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mightyiam%2Fmichie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mightyiam%2Fmichie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mightyiam","download_url":"https://codeload.github.com/mightyiam/michie/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mightyiam%2Fmichie/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30139372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T16:58:46.102Z","status":"ssl_error","status_checked_at":"2026-03-05T16:58:45.706Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crate","memoization","rust"],"created_at":"2025-08-30T15:41:02.601Z","updated_at":"2026-03-05T17:42:12.575Z","avatar_url":"https://github.com/mightyiam.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Version](https://img.shields.io/crates/v/michie)][crates.io]\n[![License](https://img.shields.io/crates/l/michie)][license]\n![Downloads](https://img.shields.io/crates/d/michie)\n![Recent downloads](https://img.shields.io/crates/dr/michie)\n[![CI status](https://img.shields.io/github/actions/workflow/status/mobusoperandi/michie/ci.yml?branch=master)][ci]\n\nmichie (sounds like Mickey) — an attribute macro that adds [memoization] to a function.\n\n\u003c!-- TOC --\u003e\n# Table of contents\n\n1. [Features](#features)\n1. [Non-features](#non-features)\n1. [key_expr](#key_expr)\n1. [store_type](#store_type)\n1. [store_init](#store_init)\n1. [Type requirements](#type-requirements)\n    1. [General bounds](#general-bounds)\n    1. [Store bounds](#store-bounds)\n1. [Generic functions](#generic-functions)\n1. [Functions that take no input](#functions-that-take-no-input)\n1. [How it works](#how-it-works)\n1. [Why must key_expr be provided](#why-must-key_expr-be-provided)\n1. [Support and feedback](#support-and-feedback)\n\u003c!-- TOC --\u003e\n\n# Features\n\n- Supports\n    - Plain functions\n    - Generic functions\n    - Functions in `impl` blocks\n    - Functions in trait implementation blocks\n    - Functions that are default trait implementations\n- Thread safe\n- Expansion depends on only `std`\n- Hygienic\n- Supports recursive functions\n- Bring your own store\n\n# Non-features\n\n- Caching features: this crate does not provide a caching mechanism other than some trivial implementations.\n  It allows you to bring your own.\n- \"Blazingly fast\": this crate aims to provide a simple and easy-to-use means of memoizing a function.\n  If you *actually really* require micro-optimized memoization then you'd most likely have to implement it yourself.\n\n# key_expr\n\nIn each invocation a key is obtained.\nIt is used to query the function's cache store for a possible hit.\nAn expression that evaluates into a key must be provided via the `key_expr` argument.\nThe expression may use bindings from the function's parameters.\nIn the following example the `key_expr` is simply the name of the only parameter.\n\n```rust\nuse michie::memoized;\nuse std::collections::HashMap;\n#[memoized(key_expr = input, store_type = HashMap\u003cusize, usize\u003e)]\nfn f(input: usize) -\u003e usize {\n    // expensive calculation\n    # unimplemented!()\n}\n```\n\n# store_type\n\nA concrete store type must be either provided via the `store_type` argument or inferred from the `store_init` (next section).\n\nThe provided type must implement [`MemoizationStore`].\nImplementations of [`MemoizationStore`] for [`BTreeMap`] and [`HashMap`] are provided.\nIn the following example, [`BTreeMap`] is provided as the store:\n\n```rust\nuse michie::memoized;\nuse std::collections::BTreeMap;\n#[memoized(key_expr = input, store_type = BTreeMap\u003cusize, usize\u003e)]\nfn f(input: usize) -\u003e usize {\n    // expensive calculation\n    # unimplemented!()\n}\n```\n\n# store_init\n\nBy default, the store is initialized via [`Default::default()`].\nDifferent initialization may be provided via an expression to `store_init`:\n\n```rust\nuse michie::memoized;\nuse std::collections::HashMap;\n#[memoized(key_expr = input, store_init = HashMap::\u003cusize, usize\u003e::with_capacity(500))]\nfn f(input: usize) -\u003e usize {\n    // expensive calculation\n    # unimplemented!()\n}\n```\n\n# Type requirements\n\nBounds apply to the key type and the function's return type.\nSome are from the general instrumentation and others are via the store type's implementation of [`MemoizationStore`].\n\n## General bounds\n\nThe following apply to the key type and to the function's return type:\n\n- [`Sized`]: for one, the instrumentation stores the key in a `let` binding.\n- [`'static`]: key and return values are owned by a store which is owned by a static.\n- [`Send`] and [`Sync`]: for parallel access.\n\n## Store bounds\n\nAnother source of bounds on the key type and the return type is the implementation of [`MemoizationStore`] for the store type.\n\n# Generic functions\n\nBe mindful of the [type requirements](#type-requirements) when using on a generic function:\n\n```rust\nuse michie::memoized;\nuse std::hash::Hash;\nuse std::collections::HashMap;\n#[memoized(key_expr = input.clone(), store_type = HashMap\u003cA, B\u003e)]\nfn f\u003cA, B\u003e(input: A) -\u003e B\nwhere\n    A: 'static + Send + Sync // bounds from instrumentation\n        + Eq + Hash // store-specific bounds\n        + Clone, // used in this function's `key_expr`\n    B: 'static + Send + Sync + Clone // bounds from instrumentation\n        + From\u003cA\u003e, // used in this function's body\n{\n    input.into()\n}\n```\n\n# Functions that take no input\n\nFunctions that take no input are good candidates for [compile-time evaluation],\nwhich is usually preferred over runtime caching (such as this crate provides).\nNonetheless, some functions cannot be evaluated at compile time.\nA reasonable `key_expr` for a function that takes no input is `()`:\n\n```rust\nuse michie::memoized;\nuse std::collections::HashMap;\n#[memoized(key_expr = (), store_type = HashMap\u003c(), f64\u003e)]\nfn f() -\u003e f64 {\n    // expensive calculation\n    # unimplemented!()\n}\n```\n\n# How it works\n\nThe original function expands into something similar to this:\n\n```rust ignore\nfn f(input: Input) -\u003e Output {\n    static STORE = Mutex::new(#store_init);\n    let key = #key_expr;\n    let store_mutex_guard = STORE.lock().unwrap();\n    let attempt = store_mutex_guard.get(\u0026key);\n    drop(store_mutex_guard);\n    if let Some(hit) = attempt {\n        return hit;\n    } else {\n        let miss = #original_fn_body;\n        let miss = STORE.lock().unwrap().insert(key, miss);\n        return miss;\n    };\n}\n```\n\n# Why must key_expr be provided\n\nThe only conceivable default `key_expr` is the entire input.\nFor example, for a function signature:\n```text\nfn f(a: usize, _b: usize) -\u003e usize\n```\nthe default `key_expr` would be `(a, _b)`.\nTwo potential problems: some parameters might not satisfy [bounds on the key type](#type-requirements).\nAlso, the resulting key might be a supervalue of _the input of the_ __actual__ _calculation_.\nTo explain the latter problem, here is an example:\n\n```rust\nuse michie::memoized;\nuse std::collections::HashMap;\n// pretend that `key_expr` is omitted and that this is the default\n#[memoized(key_expr = (a, _b), store_type = HashMap\u003c(usize, usize), usize\u003e)]\nfn f(a: usize, _b: usize) -\u003e usize {\n    // the actual calculation uses a subvalue of the input — only `a`\n    # a\n}\nf(0, 0); // expected miss because it's the first invocation\nf(0, 1); // avoidable miss!\n```\n\nIf an accurate `key_expr = a` had been provided, the second execution would have been a hit.\nTo summarize, `key_expr` is mandatory in order to encourage proper consideration of it.\n\n# Support and feedback\n\nIn [the GitHub Discussions].\n\n[`'static`]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html#trait-bound\n[compile-time evaluation]: https://doc.rust-lang.org/std/keyword.const.html#compile-time-evaluable-functions\n[memoization]: https://en.wikipedia.org/wiki/Memoization\n[crates.io]: https://crates.io/crates/michie\n[ci]: https://github.com/mobusoperandi/michie/actions/workflows/ci.yml\n[license]: https://tldrlegal.com/license/mit-license\n[the GitHub Discussions]: https://github.com/mobusoperandi/michie/discussions\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmightyiam%2Fmichie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmightyiam%2Fmichie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmightyiam%2Fmichie/lists"}