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

Introduction: Why Caching in Rust?

\n

Speed is Rust's main advantage. But even the fastest code can hit a bottleneck with slow I/O: database queries, external APIs, or complex data computations. Caching solves this problem by storing the results of expensive operations in fast storage. Rust offers flexible tools for caching: from simple built-in data structures to powerful distributed systems like Redis and Memcached.

\n

In this article, we'll cover all levels of caching in Rust: from a simple HashMap to professional solutions with TTL (time-to-live) and synchronization. You'll learn how to choose the right tool for your project and avoid common mistakes.

\n\n

1. Built-in Caching: HashMap and Its Variants

\n

The simplest way to cache in Rust is to use HashMap from the standard library. However, in a multithreaded environment, additional synchronization mechanisms are needed.

\n\n

1.1. Simple HashMap (Single-threaded)

\n

For single-threaded applications, a regular HashMap works. Example:

\n
use std::collections::HashMap;\n\nfn main() {\n    let mut cache: HashMap<String, String> = HashMap::new();\n    cache.insert("user:1".to_string(), "Alice".to_string());\n    \n    if let Some(name) = cache.get("user:1") {\n        println!("Cached value: {}", name);\n    }\n}
\n

Pros: minimal code, no dependencies.
\nCons: no TTL, not thread-safe, memory is unbounded.

\n\n

1.2. Thread-safe Cache with Mutex and Arc

\n

For multithreaded applications, use Arc<Mutex<HashMap>>:

\n
use std::collections::HashMap;\nuse std::sync::{Arc, Mutex};\nuse std::thread;\n\nstruct Cache {\n    store: Arc<Mutex<HashMap<String, String>>>,\n}\n\nimpl Cache {\n    fn new() -> Self {\n        Cache { store: Arc::new(Mutex::new(HashMap::new())) }\n    }\n    \n    fn get(&self, key: &str) -> Option<String> {\n        self.store.lock().ok()?.get(key).cloned()\n    }\n    \n    fn set(&self, key: String, value: String) {\n        self.store.lock().unwrap().insert(key, value);\n    }\n}\n\nfn main() {\n    let cache = Cache::new();\n    let c1 = cache.clone();\n    thread::spawn(move || {\n        c1.set("key".to_string(), "value".to_string());\n    });\n    println!("{:?}", cache.get("key"));\n}
\n

Drawback: Mutex locking can reduce performance under high contention.

\n\n

1.3. Advanced Libraries: lru and cached

\n

For production code, it's better to use ready-made crates:

\n
    \n
  • lru — implements the LRU (Least Recently Used) eviction algorithm. Suitable for bounded caches.
  • \n
  • cached — provides macros for automatic function caching with TTL and LRU support.
  • \n
\n

Example with cached:

\n
use cached::proc_macro::cached;\n\n#[cached(size = 100, time = 300)]\nfn expensive_computation(x: u32) -> u32 {\n    std::thread::sleep(std::time::Duration::from_secs(2));\n    x * x\n}\n\nfn main() {\n    let result = expensive_computation(5); // takes 2 sec\n    let cached_result = expensive_computation(5); // instant\n    println!("{}", cached_result);\n}
\n\n

2. Redis: Distributed Cache for Scalable Systems

\n

Redis is an in-memory data store, ideal for distributed caching. In Rust, the redis crate is used to work with Redis.

\n\n

2.1. Connection and Basic Operations

\n
use redis::Commands;\n\nfn main() -> redis::RedisResult<()> {\n    let client = redis::Client::open("redis://127.0.0.1:6379/")?;\n    let mut con = client.get_connection()?;\n    \n    // Setting a value with TTL (time-to-live)\n    con.set_ex("user:1", "Alice", 3600)?; //                

Blogs

Book Recommendations