Moka Integration with async-std: A Complete Runtime Guide
Moka's asynchronous cache works seamlessly with async-std by enabling the future feature, which provides runtime-agnostic Future implementations compatible with any async executor.
The moka-rs/moka crate provides high-performance, concurrent caching for Rust applications, and integrating Moka with async-std requires only the future feature flag to unlock async cache operations. This integration leverages the standard std::future::Future trait to ensure compatibility without runtime-specific dependencies.
How Moka Supports async-std
Moka's async support is implemented in the future module, available when the future crate feature is enabled. Unlike some async libraries that hardcode runtime dependencies, Moka's cache is runtime-agnostic—it returns impl Future types that any executor can drive, including async-std's task scheduler.
Enabling the future Feature
To use Moka with async-std, activate the feature in Cargo.toml:
[dependencies]
moka = { version = "0.12", features = ["future"] }
async-std = { version = "1", features = ["attributes"] }
This feature flag pulls in optional dependencies (async-lock, event-listener, futures-util) and compiles the src/future/cache.rs module, which contains the async-aware cache implementation.
Runtime-Agnostic Architecture
In src/future/cache.rs, the Cache type implements Send (via unsafe impl<K, V, S> Send for Cache at line 641) and Sync, allowing safe sharing across async-std tasks. All async methods—including insert, get, and invalidate—return futures that are Send + Sync, satisfying async-std's task requirements without additional synchronization primitives.
As documented in the repository's README.md, the future feature must be specified to use the cache under async runtimes such as tokio or async-std.
Code Examples for async-std Integration
Basic async-std Usage with #[async_std::main]
The simplest integration uses async-std's main macro to run cache operations directly. This pattern is documented in MIGRATION-GUIDE.md (lines 136-166):
use std::sync::Arc;
use moka::future::Cache;
#[async_std::main]
async fn main() {
// Create a future-aware cache that can hold 10_000 entries.
let cache = Arc::new(Cache::new(10_000));
// Insert a value asynchronously.
cache.insert("hello", "world".to_owned()).await;
// Retrieve it later.
if let Some(v) = cache.get(&"hello").await {
println!("Got: {v}");
}
}
Blocking from Synchronous Code with block_on
When calling Moka from synchronous contexts within an async-std application, use async_std::task::block_on to drive the futures:
use std::sync::Arc;
use async_std::task;
use moka::future::Cache;
fn main() {
let cache = Arc::new(Cache::new(100));
// Spawn a native OS thread that inserts a value.
let cache_cloned = Arc::clone(&cache);
std::thread::spawn(move || {
// `block_on` drives the future on async-std's executor.
task::block_on(cache_cloned.insert(42, "answer".to_owned()));
})
.join()
.unwrap();
// Verify from the normal thread (still uses block_on).
let value = task::block_on(cache.get(&42));
assert_eq!(value, Some("answer".to_owned()));
}
This approach, identical to the block-on example in MIGRATION-GUIDE.md, allows synchronous initialization code to populate the cache before spawning async tasks.
Concurrent Access Across Multiple Tasks
Because Cache is both Send and Sync, you can share it across many concurrent async-std tasks using Arc:
use async_std::task;
use std::sync::Arc;
use moka::future::Cache;
#[async_std::main]
async fn main() {
const N: usize = 8;
let cache = Arc::new(Cache::new(1_000));
// Launch several async tasks that concurrently insert entries.
let mut handles = Vec::new();
for i in 0..N {
let c = Arc::clone(&cache);
handles.push(task::spawn(async move {
c.insert(i, format!("value-{i}")).await;
}));
}
// Wait for all tasks to finish.
for h in handles {
h.await;
}
// All entries are now present.
for i in 0..N {
assert_eq!(cache.get(&i).await, Some(format!("value-{i}")));
}
}
Each task receives a cloned Arc<Cache>, enabling cheap, lock-free sharing of the cache state across async-std's task scheduler.
Key Source Files and Implementation Details
Understanding these files clarifies how Moka achieves async-std compatibility:
Cargo.toml– Defines thefuturefeature flag that enables async cache support and pulls in required dependencies (async-lock,event-listener,futures-util).src/future/cache.rs– Contains the coreCacheimplementation, including theSendtrait implementation (line 641) that guarantees thread safety across async-std tasks.MIGRATION-GUIDE.md– Provides concrete async-std usage patterns, including theasync_std::task::block_onsnippet and#[async_std::main]examples.README.md– Documents supported runtimes (Tokio, async-std, Actix-rt) and explains that thefuturefeature is required for async runtime integration.
Summary
- Enable the
futurefeature inCargo.tomlto compile Moka's async cache module. - Use
moka::future::Cachefor all async operations; it returns standardFuturetypes compatible with async-std. - The cache implements
Send + Sync, allowing safe sharing acrossasync_std::task::spawnwithout additional synchronization. - From synchronous code, use
async_std::task::block_onto drive cache futures on async-std's executor. - No runtime-specific code is required in
src/future/cache.rs, making the integration runtime-agnostic.
Frequently Asked Questions
Does Moka require Tokio to work with async-std?
No. According to the moka-rs/moka source code, the cache is runtime-agnostic. When the future feature is enabled, the cache works with any executor implementing std::future::Future, including async-std, Tokio, or Actix-rt. The README.md explicitly lists async-std as a supported runtime.
How do I enable async support in Moka?
Add features = ["future"] to your Cargo.toml dependency declaration for moka. This activates the future module in src/future/cache.rs and pulls in necessary async dependencies (async-lock, event-listener, futures-util) without binding to a specific runtime.
Is Moka's async cache thread-safe for async-std tasks?
Yes. The Cache type in src/future/cache.rs implements both Send and Sync (see the unsafe impl block at line 641), meaning it can safely be shared between async-std tasks using Arc and accessed concurrently without data races.
Can I use Moka from synchronous code when using async-std?
Yes. Use async_std::task::block_on to execute cache operations from synchronous contexts. As shown in MIGRATION-GUIDE.md, this pattern allows you to bridge sync and async code when pre-populating the cache or accessing it from non-async threads.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →