Introdução: por que armazenar em cache no Rust?
Rust é uma linguagem de programação de sistemas valorizada por seu desempenho e segurança de memória. No entanto, mesmo os programas mais rápidos podem ficar lentos com operações repetitivas de I/O, cálculos complexos ou consultas frequentes a bancos de dados. O cache permite salvar os resultados de operações dispendiosas e retorná-los instantaneamente, o que é crítico para serviços web de alta carga, motores de jogos e sistemas de tempo real.
Neste artigo, exploraremos três abordagens para cache em Rust: sistemas externos (Redis, Memcached) e soluções embutidas (lru-rs, moka). Você aprenderá como integrá-los ao código, quais problemas cada ferramenta resolve e como escolher a opção ideal para seu projeto.
1. Cache externo: Redis e Memcached
Sistemas de cache externos funcionam como serviços separados, aos quais a aplicação se conecta pela rede. Eles são ideais para sistemas distribuídos, onde vários processos ou servidores precisam compartilhar um cache comum.
1.1. Redis através da crate redis
Redis é o in-memory store mais popular, suportando estruturas de dados complexas (listas, conjuntos, hashes). Para conectar-se ao Redis em Rust, utiliza-se a crate redis.
Instalação: adicione ao Cargo.toml:
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }Exemplo de cache básico de string:
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?;
// Gravação no cache por 60 segundos let _: () = con.set_ex("user:42", "Alice", 60).await?; // Leitura let val: Option = con.get("user:42").await?; println!("Valor em cache: {:?}", val); Ok(())} Redis é excelente para cache de sessões, respostas de API e filas de tarefas. Sua desvantagem é a necessidade de instalar e manter um servidor separado.
1.2. Memcached através da crate memcache
Memcached é uma alternativa mais leve ao Redis, focada em armazenamento simples key-value. A crate memcache fornece API síncrona e assíncrona.
Exemplo com a crate memcache:
use memcache::{Client, MemcacheError};
fn main() -> Result<(), MemcacheError> { let client = Client::connect("memcache://127.0.0.1:11211")?; client.set("counter", 100, 30)?; // TTL = 30 segundos let val: Option = client.get("counter")?; println!("Valor: {:?}", val); Ok(())} Memcached é mais rápido que Redis em operações simples de get/set, mas não suporta estruturas de dados complexas. Escolha-o se você precisar apenas de "chave-valor" com sobrecarga mínima.
2. Bibliotecas de cache embutidas
Para aplicações de processo único (utilitários CLI, programas desktop, servidores de jogos), o cache externo é excessivo. Soluções embutidas operam na memória do processo, não exigem rede e são extremamente rápidas.
2.1. lru-rs — cache LRU simples
A crate lru implementa um cache com política de despejo Least Recently Used. É ideal para armazenar em cache resultados de parsing, configurações ou objetos pequenos.
use lru::LruCache;use std::num::NonZeroUsize;
fn main() { let mut cache: LruCache = LruCache::new(NonZeroUsize::new(3).unwrap()); cache.put("a", 1); cache.put("b", 2); cache.put("c", 3); cache.put("d", 4); // "a" será despejado, pois o cache está cheio
println!("{:?}", cache.get(&"a")); // None println!("{:?}", cache.get(&"d")); // Some(4)} Vantagens: minimalista, tempo de execução previsível, thread-safe ao usar