Cache de dados em Rust: quando a velocidade decide tudo
\nImagine: sua aplicação Rust processa milhares de requisições por segundo, mas toda vez recalcula os mesmos dados. O banco de dados geme, os usuários esperam, e você sente o desempenho cair. Situação familiar? O cache é aquela boia salva-vidas que transforma uma aplicação lenta em um motor a jato. Neste artigo, vamos entender como fazer cache de dados em Rust de forma eficiente, usando recursos nativos e crates populares. Nada de mágica — apenas código puro e rápido.
\nRust é famoso por seu desempenho, mas nem ele está imune a gargalos. O cache em Rust não é apenas uma técnica, mas uma filosofia: não faça trabalho desnecessário. Vamos explorar várias abordagens: desde o simples HashMap até o avançado LRU cache e soluções para cenários multithread. Prepare o compilador — vamos nessa!
Por que o cache de dados em Rust é indispensável?
\nNo mundo da programação de sistemas, o cache de dados em Rust resolve três problemas principais:
\n- \n
- Redução de latência — o acesso à memória é 100-1000 vezes mais rápido do que ler do disco ou fazer uma requisição de rede. \n
- Economia de recursos — menos carga na CPU, no banco de dados e em APIs externas. \n
- Escalabilidade — o cache permite suportar picos de carga sem colapso. \n
Mas Rust adiciona suas particularidades: o sistema rigoroso de ownership e borrowing exige cuidado ao trabalhar com dados compartilhados. Se você programa em Rust, já está acostumado ao controle, mas o cache é um caso onde o controle precisa ser um pouco relaxado (com segurança, é claro).
\n\nCache básico com HashMap: simples e eficiente
\nA maneira mais óbvia de fazer cache de dados em Rust é usar HashMap. Ele é ideal para pequenos volumes de dados, onde as chaves são únicas e o tempo de vida do cache é limitado a uma única thread.
Aqui está um exemplo de um cache simples para resultados de cálculos:
\nuse std::collections::HashMap;\n\nstruct Cache {\n store: HashMap<String, i32>,\n}\n\nimpl Cache {\n fn new() -> Self {\n Cache {\n store: HashMap::new(),\n }\n }\n\n fn get_or_compute(&mut self, key: &str, compute: fn() -> i32) -> i32 {\n if let Some(&value) = self.store.get(key) {\n return value;\n }\n let value = compute();\n self.store.insert(key.to_string(), value);\n value\n }\n}\n\nfn main() {\n let mut cache = Cache::new();\n let result = cache.get_or_compute("expensive_data", || {\n // Simulação de cálculo pesado\n std::thread::sleep(std::time::Duration::from_millis(100));\n 42\n });\n println!("Resultado: {}", result);\n}\nEste código demonstra o básico: verifica se a chave existe, se não, calcula e armazena. Mas essa abordagem tem desvantagens: o cache cresce sem controle e não há mecanismo de limpeza. Para produção, é preciso algo mais inteligente.
\n\nQuando o HashMap falha
\nImagine que sua aplicação funciona por dias. O HashMap vai crescer até consumir toda a memória. A solução é o LRU cache (Least Recently Used), que remove registros antigos.
\n\nLRU cache em Rust: solução elegante para memória limitada
\nO LRU cache é o padrão ouro para cache de dados em Rust. Ele armazena um número fixo de elementos e remove automaticamente os menos acessados. Em Rust, existe a excelente crate lru para isso.
Adicione ao Cargo.toml:
[dependencies]\nlru = "0.12"\nAgora um exemplo de uso:
\nuse lru::LruCache;\nuse std::num::NonZeroUsize;\n\nfn main() {\n // Cache para 3 elementos\n let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());\n\n cache.put("um", 1);\n cache.put("dois", 2);\n cache.put("três", 3);\n\n // Acessa "um" para torná-lo "r