Introduction to Caching in Rust
Caching is one of the key tools for improving the performance of Rust applications. Whether you are building a high-load web server, a database, or a desktop application, proper caching allows you to reduce response time, lower the load on external resources, and optimize memory usage. In this article, we will cover three main approaches to caching in the Rust ecosystem: using external systems (Redis and Memcached) and built-in solutions such as lru-rs and moka. You will learn when to choose which tool and get ready-to-use code examples.
External Caching Systems: Redis and Memcached
Caching with Redis in Rust
Redis is a popular in-memory data store that supports various data structures (strings, lists, sets, hash tables). The redis crate (or redis-rs) is used to work with Redis in Rust.
Add the dependency to Cargo.toml:
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }serde = { version = "1", features = ["derive"] }serde_json = "1"Example of connecting and basic caching with Redis:
use redis::AsyncCommands;use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]struct User { id: u32, name: String, email: String,}
#[tokio::main]async fn main() -> redis::RedisResult<()> { let client = redis::Client::open("redis://127.0.0.1:6379/")?; let mut conn = client.get_multiplexed_async_connection().await?;
// Save object to cache (serialize to JSON) let user = User { id: 1, name: "Alexey".into(), email: "alex@example.com".into(), }; let json = serde_json::to_string(&user).unwrap(); conn.set_ex("user:1", json, 3600).await?; // TTL = 1 hour
// Read from cache let cached: String = conn.get("user:1").await?; let user_cached: User = serde_json::from_str(&cached).unwrap(); println!("User retrieved: {:?}", user_cached);
Ok(())}Pros of Redis: support for complex data structures, TTL, pub/sub, replication, clustering. Cons: requires a separate server, network latency.
Caching with Memcached in Rust
Memcached is a lighter and simpler in-memory store focused on storing key-value pairs. The memcache crate is used for Rust.
Add to Cargo.toml:
[dependencies]memcache = "0.19"tokio = { version = "1", features = ["full"] }Example of working with Memcached:
use memcache::Client;
#[tokio::main]async fn main() -> memcache::MemcacheResult<()> { let client = Client::connect("memcache://127.0.0.1:11211")?;
// Write with TTL (in seconds) client.set("greeting", "Hello, world!", 300)?;
// Read let value: Option<String> = client.get("greeting")?; if let Some(msg) = value { println!("From cache: {}", msg); }
// Increment counter client.set("counter", "10", 0)?; client.increment("counter", 5)?; let counter: Option<u64> = client.get("counter")?; println!("Counter: {:?}", counter);
Ok(())}Pros of Memcached: simplicity, high speed, minimal memory consumption. Cons: no complex data structures, only string/byte storage.
Built-in Caching Solutions in Rust
lru-rs Library (LRU Cache)
The lru crate implements the classic Least Recently Used (LRU) eviction algorithm. It is ideal for in-memory caching.