Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
C

cached

> 编程语言
Open source

Rust cache structures and easy function memoization

2.1K stars0 likes1 views
WebsiteGitHub

About

Rust cache structures and easy function memoization

cached

Caching structures and simplified function memoization

cached provides implementations of several caching structures as well as macros for defining memoized functions.

When using the store types directly, start with use cached::prelude::*;. The short method names (get/set/len/...) live on blanket extension traits, and the prelude brings every trait in with one import (see Method naming below).

Requires Rust >= 1.92. (The async_core / async features do not compile before 1.92: the CachedGetOrSetAsync default bodies hit a borrowck limitation rustc attributes to rust-lang/rust#100013. rust-version is a single crate-level value, so the floor applies to every feature set.)

Memoized functions defined using #[cached]/#[once] macros are thread-safe with the backing function-cache wrapped in a mutex/rwlock. #[concurrent_cached] functions are thread-safe via the store's own internal synchronization: sharded stores use per-shard parking_lot::RwLock; Redis and disk stores rely on their respective server/file-system concurrency. By default, #[cached] uses no write synchronization: concurrent uncached calls for the same key may each compute independently and overwrite each other, matching the 2.x behavior and Python's functools.lru_cache. Set sync_writes = "by_key" to deduplicate concurrent first calls for the same key through bucketed per-key locks. Set sync_writes = true (or "default") to hold the whole-cache lock for the duration of each miss. Note: "by_key" holds the per-key bucket lock across the entire function body, so it must not be used on recursive or re-entrant memoized functions (deadlock risk when keys in the active call chain share a bucket). #[once] defaults to no synchronization (add sync_writes = true to serialize concurrent first-calls); #[concurrent_cached] does not support sync_writes. The number of per-key lock buckets for "by_key" is tunable with sync_writes_buckets = N (default 64).

  • See cached::stores docs for the available cache stores.
  • See macros docs for more macro examples.

Upgrading from 2.x? See the migration guide for a step-by-step walkthrough, or the agent-oriented guide for the complete breaking-change list.

Upgrading from 1.x? 2.0 contains breaking changes (new cache_remove_entry required method, Result/Option caching behavior flipped to smart-by-default, result/option attributes removed, and more). See the 2.0 migration guide for a step-by-step walkthrough.

Upgrading from a pre-1.0 release? 1.0 contains breaking changes (store renames, removed declarative macros, renamed macro/builder attributes, and a changed Redis key format). See the 1.0 migration guide for a step-by-step walkthrough, or the agent-oriented guide for automated migration tooling.

Method naming

Every synchronous cache operation has a short alias (get/set/remove/clear/len/...) and a cache_-prefixed form (cache_get/cache_set/cache_remove/cache_clear/cache_size/...). The short aliases are the preferred spelling. Use the cache_-prefixed names when a short alias would collide with another in-scope trait's method of the same name (for example, your type also implements a trait with its own get).

The get/set/remove short aliases for Cached stores live on CachedExt; those for ConcurrentCached stores live on ConcurrentCachedExt. Both extension traits have blanket implementations, so the short names are always available when the extension trait is in scope. The simplest way to get them is use cached::prelude::*;, which re-exports both extension traits. Alternatively, import them directly: use cached::{Cached, CachedExt};. Custom store implementations only need to implement the cache_-prefixed required methods on the core trait; the short aliases come for free via the blanket extension trait impl.

For Cached stores, len/is_empty are also on CachedExt. For ConcurrentCached stores, size introspection is cache_size and cache_is_empty on ConcurrentCacheBase (the shared base trait), not on ConcurrentCachedExt: bring ConcurrentCacheBase into scope to call them on a generic bound. Note that ConcurrentCacheBase::cache_size returns Result<Option<usize>, _>: fallible for IO-backed stores, and None when the backend cannot report an exact count. The sharded stores keep their inherent infallible len/is_empty too, which take priority at the call site.

Both async traits use the async_cache_* spelling. ConcurrentCachedAsync mirrors the sync ConcurrentCached surface (async_cache_get, async_cache_set, async_cache_remove, ...) for concurrent stores that manage their own synchronization (the in-memory sharded stores, plus the IO-backed redis and redb stores). CachedGetOrSetAsync is narrower: it only memoizes an async closure over a synchronous in-memory Cached store, via the async_cache_get_or_set_with family (async_cache_get_or_set_with, async_cache_try_get_or_set_with, and their _mut variants).

ConcurrentCachedAsync has its own deduplicated aliases on ConcurrentCachedAsyncExt (async_get, async_set, async_remove, async_remove_entry, async_delete, async_contains, async_clear, async_reset, async_get_or_set_with, async_try_get_or_set_with). They keep the async_ prefix instead of being bare get/set, because the stores implementing ConcurrentCachedAsync also implement the synchronous ConcurrentCached: bare names would be a second applicable candidate alongside ConcurrentCachedExt and make store.get(&k) ambiguous. Size and metric introspection is not aliased there; it is cache_size / cache_is_empty and the metric readers on ConcurrentCacheBase, which are callable on an async store with no extension trait imported. CachedGetOrSetAsync has no alias trait; its async_ prefix already prevents collisions with the sync methods.

Features

  • default: Include proc_macro, ahash, and time_stores features
  • proc_macro: Include proc macros
  • ahash: Enable the optional ahash hasher as default hashing algorithm.
  • async_core: Async trait definitions (the runtime-agnostic async cache traits) without the async-lock dependency. Enabled by async.
  • async: Include support for async functions and async cache stores (runtime-agnostic; no tokio dependency; uses async-lock)
  • redis_store: Include Redis cache store
  • redis_smol: Include async Redis support using smol (no TLS); implies redis_store and async
  • redis_smol_native_tls: redis_smol + TLS via native-tls (system TLS library)
  • redis_smol_rustls: redis_smol + TLS via rustls (pure-Rust TLS)
  • redis_tokio: Include async Redis support using tokio (no TLS); implies redis_store and async
  • redis_tokio_native_tls: redis_tokio + TLS via native-tls (system TLS library)
  • redis_tokio_rustls: redis_tokio + TLS via rustls (pure-Rust TLS)
  • redis_connection_manager: Enable the optional connection-manager capability of redis. Additive: async redis caches keep using a MultiplexedConnection by default; opt a specific cache into the auto-reconnecting connection manager with .connection_manager(true) on its builder. This capability feature pulls in redis_store and async itself, but it is runtime-agnostic (redis/connection-manager needs only redis/aio), so it carries no runtime: pair it with a runtime feature (redis_tokio* or redis_smol*) to actually connect; enabling it alone leaves you without a runtime. Does not enable TLS.
  • redis_async_cache: Enable Redis client-side caching over RESP3 for async Redis caches. Implies async and redis_store, but is runtime-agnostic (redis/cache-aio needs only redis/aio): pair it with a runtime feature (redis_tokio* or redis_smol*) or the build has no runtime to connect with. Does not enable TLS.
  • redb_store: Include disk cache store
  • time_stores: Include time-based cache stores (TtlCache, LruTtlCache, TtlSortedCache, ShardedTtlCache, and ShardedLruTtlCache). Also required when using #[cached(ttl_secs = ...)], #[cached(ttl = ...)], #[cached(ttl_millis = ...)], #[concurrent_cached(ttl_secs = ...)], #[concurrent_cached(ttl = ...)], or #[concurrent_cached(ttl_millis = ...)] on the default in-memory path. (#[once] has its own ungated timer, so #[once(ttl_secs = ...)] does NOT require this feature.) Disable this feature when targeting environments without system time support (e.g. wasm32-unknown-unknown without WASI or JS).

The procedural macros (#[cached], #[once], #[concurrent_cached]) offer a number of features, including async support. See the macros module for more samples, and the examples directory for runnable snippets.

Any custom cache that implements cached::Cached can be used with the #[cached]/#[once] macros in place of the built-ins (cached::CachedGetOrSetAsync additionally memoizes an async closure over such a store). Any custom cache that implements cached::ConcurrentCached/cached::ConcurrentCachedAsync can be used with the #[concurrent_cached] macro.

Macro quick reference

Use case Annotated signature
#[cached]
Unbounded memoize (default; concurrent misses each compute independently) #[cached] fn fib(n: u64) -> u64
Unbounded memoize, explicit no-sync (same as default) #[cached(sync_writes = false)] fn fib(n: u64) -> u64
LRU-bounded — evict past N entries #[cached(max_size = 1_000)] fn lookup(id: u32) -> Row
TTL — expire results after N whole seconds #[cached(ttl_secs = 60)] fn config() -> Config
TTL as a Duration expression (inlined verbatim, so Duration must be in scope; see note below) #[cached(ttl = "Duration::from_secs(60)")] fn config() -> Config
TTL in milliseconds (sub-second capable; Redis honors millisecond TTL via PSETEX/PEXPIRE) #[cached(ttl_millis = 500)] fn poll(id: u64) -> Status
LRU + TTL #[cached(max_size = 500, ttl_secs = 300)] fn search(q: String) -> Vec<Hit>
Don't cache None returns (implicit for Option<T>) #[cached] fn find(id: u64) -> Option<User>
Don't cache Err returns (implicit for Result<T, E>) #[cached] fn load(id: u64) -> Result<Data, E>
Force-cache None returns #[cached(cache_none = true)] fn find(id: u64) -> Option<User>
Force-cache Err returns #[cached(cache_err = true)] fn load(id: u64) -> Result<Data, E>
Serve stale value when function returns Err #[cached(result_fallback = true, ttl_secs = 60)] fn fetch(id: u64) -> Result<Data, E>
Per-value / dynamic per-entry TTL (value carries its own expiry) #[cached(expires = true)] fn token(scope: String) -> Token
Deduplicate concurrent first calls per key (opt-in; do not use on recursive functions) `#[cached(ttl_secs = 30, sync_writes = "by_key")] fn expensive(id:

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rustcachecachinglrucachememoization

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言