Caching in Rust: Redis, Memcached, and Built-in Solutions

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Caching in Rust: Redis and Other Efficient Solutions



In modern high-load development with Rust, caching plays a key role. It reduces the load on databases, speeds up API responses, and optimizes work with expensive computations. In this article, we will look at the main libraries for caching in the Rust ecosystem: from powerful Redis to lightweight in-memory solutions.



1. Redis via the redis-rs Crate



redis-rs is the primary and most mature library for working with Redis in Rust. It supports asynchronous operations (via tokio or async-std), clustering, pub/sub, and all popular Redis data types.



Installation

[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }


Basic Example: Caching Strings

use redis::AsyncCommands;

#[tokio::main]async fn main() -> redis::RedisResult<()> { let client = redis::Client::open("redis://127.0.0.1:6379/")?; let mut con = client.get_multiplexed_async_connection().await?;

// Set cache with TTL of 60 seconds let _: () = con.set_ex("user:123", "{\\"name\\":\\"Alice\\"}", 60).await?;

// Get the value let cached: String = con.get("user:123").await?; println!("Cached data: {}", cached);

// Check if key exists let exists: bool = con.exists("user:123").await?; println!("Key exists: {}", exists);

Ok(())}


Serializing Complex Structures

To cache data structures, use serde together with redis:

use serde::{Serialize, Deserialize};use redis::AsyncCommands;

#[derive(Debug, Serialize, Deserialize)]struct User { id: u32, name: String, email: String,}

async fn cache_user(con: &mut redis::aio::MultiplexedConnection, user: &User) { let json = serde_json::to_string(user).unwrap(); let _: () = con.set_ex(format!("user:{}", user.id), json, 300).await.unwrap();}

async fn get_cached_user(con: &mut redis::aio::MultiplexedConnection, id: u32) -> Option<User> { let json: Option<String> = con.get(format!("user:{}", id)).await.unwrap(); json.and_then(|data| serde_json::from_str(&data).ok())}


2. In-memory Caching: The moka Crate



Moka is a high-performance in-memory caching library inspired by Caffeine (Java). It is ideal for scenarios where an external server is not required and maximum speed is needed.



Features of moka:

  • Automatic eviction by time (TTL, TTI) and by size
  • Asynchronous refresh (background refresh)
  • Thread-safe for multi-threaded use (Arc + RwLock)
  • Support for weighted cache policies


Example: Cache with TTL and Size Limit

use moka::sync::Cache;use std::time::Duration;

fn main() { // Create a cache for 1000 items with a TTL of 10 minutes let cache: Cache<String, String> = Cache::builder() .max_capacity(1000) .time_to_live(Duration::from_secs(600)) .build();

// Insert and retrieve cache.insert("key1".to_string(), "value1".to_string()); if let Some(v) = cache.get("key1") { println!("Value: {}", v); }}


Asynchronous Variant (moka::future::Cache)

use moka::future::Cache;

#[tokio::main]async fn main() { let cache: Cache<u64, String> = Cache::new(10_000);

// Asynchronous insertion cache.insert(42, "Hello async".to_string()).await;

// Asynchronous retrieval if let Some(val) = cache.get(&42).await { println!("{}", val); }}


3. Declarative Caching: The <

Recommendations