Visao Geral do Modulo std::any
O modulo std::any fornece mecanismos para verificacao de tipos em tempo de execucao (runtime). Em um sistema de tipos estatico como o de Rust, normalmente todos os tipos sao conhecidos em tempo de compilacao. Porem, ha cenarios em que voce precisa trabalhar com tipos desconhecidos — como sistemas de plugins, registros de eventos heterogeneos ou frameworks de injecao de dependencia.
Os dois tipos principais sao:
Any— trait que permite a qualquer tipo'staticser inspecionado e convertido (downcast) em runtimeTypeId— identificador unico de um tipo, util para comparacoes
O std::any e a resposta do Rust para cenarios que em outras linguagens seriam resolvidos com reflexao (reflection). Ele oferece uma forma controlada e segura de fazer type erasure e recuperacao.
Tipos e Funcoes Principais
Trait Any
A trait Any e implementada automaticamente para todo tipo que satisfaca 'static (ou seja, nao contem referencias com lifetimes nao-estaticos).
| Metodo | Descricao |
|---|---|
.type_id() | Retorna o TypeId do valor |
.is::<T>() | Verifica se o valor e do tipo T |
.downcast_ref::<T>() | Tenta converter para &T (retorna Option) |
.downcast_mut::<T>() | Tenta converter para &mut T (retorna Option) |
Box
| Metodo | Descricao |
|---|---|
.downcast::<T>() | Tenta converter para Box<T> (retorna Result) |
.is::<T>() | Verifica se o conteudo e do tipo T |
TypeId
| Metodo | Descricao |
|---|---|
TypeId::of::<T>() | Retorna o TypeId de um tipo |
A comparacao entre TypeIds e constante e extremamente rapida.
Exemplos Praticos
1. Verificacao de Tipos em Runtime
use std::any::Any;
fn inspecionar(valor: &dyn Any) {
if valor.is::<i32>() {
let numero = valor.downcast_ref::<i32>().unwrap();
println!("E um i32: {numero}");
} else if valor.is::<String>() {
let texto = valor.downcast_ref::<String>().unwrap();
println!("E uma String: \"{texto}\"");
} else if valor.is::<f64>() {
let decimal = valor.downcast_ref::<f64>().unwrap();
println!("E um f64: {decimal}");
} else {
println!("Tipo desconhecido (TypeId: {:?})", valor.type_id());
}
}
fn main() {
inspecionar(&42i32);
inspecionar(&String::from("Rust"));
inspecionar(&3.14f64);
inspecionar(&true); // Tipo desconhecido
}
2. Colecao Heterogenea com Box
use std::any::Any;
struct Registro {
campos: Vec<(&'static str, Box<dyn Any>)>,
}
impl Registro {
fn new() -> Self {
Registro { campos: Vec::new() }
}
fn inserir<T: Any>(&mut self, nome: &'static str, valor: T) {
self.campos.push((nome, Box::new(valor)));
}
fn obter<T: Any>(&self, nome: &str) -> Option<&T> {
self.campos
.iter()
.find(|(n, _)| *n == nome)
.and_then(|(_, v)| v.downcast_ref::<T>())
}
fn remover<T: Any>(&mut self, nome: &str) -> Option<T> {
let pos = self.campos.iter().position(|(n, _)| *n == nome)?;
let (_, caixa) = self.campos.remove(pos);
caixa.downcast::<T>().ok().map(|b| *b)
}
}
fn main() {
let mut reg = Registro::new();
reg.inserir("nome", String::from("Alice"));
reg.inserir("idade", 30u32);
reg.inserir("ativo", true);
reg.inserir("salario", 5500.0f64);
// Recuperando com tipo correto
if let Some(nome) = reg.obter::<String>("nome") {
println!("Nome: {nome}");
}
if let Some(idade) = reg.obter::<u32>("idade") {
println!("Idade: {idade}");
}
// Tipo incorreto retorna None
let errado = reg.obter::<i32>("idade"); // None (e u32, nao i32!)
println!("Tipo errado: {errado:?}");
// Removendo com ownership
if let Some(salario) = reg.remover::<f64>("salario") {
println!("Salario removido: {salario}");
}
}
3. Sistema de Plugins com Any
use std::any::{Any, TypeId};
use std::collections::HashMap;
trait Plugin: Any {
fn nome(&self) -> &str;
fn executar(&self);
fn as_any(&self) -> &dyn Any;
}
struct PluginLog {
prefixo: String,
}
impl Plugin for PluginLog {
fn nome(&self) -> &str { "Logger" }
fn executar(&self) {
println!("[{}] Plugin de log executado", self.prefixo);
}
fn as_any(&self) -> &dyn Any { self }
}
struct PluginMetrica {
contador: u64,
}
impl Plugin for PluginMetrica {
fn nome(&self) -> &str { "Metricas" }
fn executar(&self) {
println!("Metricas coletadas: {} eventos", self.contador);
}
fn as_any(&self) -> &dyn Any { self }
}
struct GerenciadorPlugins {
plugins: HashMap<TypeId, Box<dyn Plugin>>,
}
impl GerenciadorPlugins {
fn new() -> Self {
GerenciadorPlugins {
plugins: HashMap::new(),
}
}
fn registrar<P: Plugin + 'static>(&mut self, plugin: P) {
let tipo = TypeId::of::<P>();
println!("Registrando plugin: {}", plugin.nome());
self.plugins.insert(tipo, Box::new(plugin));
}
fn obter<P: Plugin + 'static>(&self) -> Option<&P> {
let tipo = TypeId::of::<P>();
self.plugins
.get(&tipo)
.and_then(|p| p.as_any().downcast_ref::<P>())
}
fn executar_todos(&self) {
for plugin in self.plugins.values() {
plugin.executar();
}
}
}
fn main() {
let mut gerenciador = GerenciadorPlugins::new();
gerenciador.registrar(PluginLog {
prefixo: "APP".into(),
});
gerenciador.registrar(PluginMetrica {
contador: 1500,
});
// Executar todos os plugins
gerenciador.executar_todos();
// Acessar um plugin especifico pelo tipo
if let Some(log) = gerenciador.obter::<PluginLog>() {
println!("Prefixo do logger: {}", log.prefixo);
}
}
4. TypeId para Comparacao Eficiente
use std::any::TypeId;
fn tipo_para_nome(id: TypeId) -> &'static str {
if id == TypeId::of::<i32>() {
"i32"
} else if id == TypeId::of::<String>() {
"String"
} else if id == TypeId::of::<Vec<u8>>() {
"Vec<u8>"
} else {
"desconhecido"
}
}
fn nome_do_tipo<T: 'static>() -> &'static str {
tipo_para_nome(TypeId::of::<T>())
}
fn main() {
println!("{}", nome_do_tipo::<i32>()); // i32
println!("{}", nome_do_tipo::<String>()); // String
println!("{}", nome_do_tipo::<Vec<u8>>()); // Vec<u8>
println!("{}", nome_do_tipo::<bool>()); // desconhecido
// TypeId e Copy, barato de comparar e pode ser usado como chave
let id1 = TypeId::of::<String>();
let id2 = TypeId::of::<String>();
let id3 = TypeId::of::<&str>();
assert_eq!(id1, id2);
assert_ne!(id1, id3); // String e &str sao tipos diferentes!
}
5. Type Erasure e Recuperacao
use std::any::Any;
struct Cache {
dados: Vec<Box<dyn Any>>,
}
impl Cache {
fn new() -> Self {
Cache { dados: Vec::new() }
}
fn armazenar<T: Any>(&mut self, valor: T) -> usize {
let indice = self.dados.len();
self.dados.push(Box::new(valor));
indice
}
fn recuperar<T: Any>(&self, indice: usize) -> Option<&T> {
self.dados.get(indice)?.downcast_ref::<T>()
}
fn recuperar_owned<T: Any>(mut self, indice: usize) -> Option<T> {
if indice >= self.dados.len() {
return None;
}
let item = self.dados.swap_remove(indice);
item.downcast::<T>().ok().map(|b| *b)
}
}
fn main() {
let mut cache = Cache::new();
let i_str = cache.armazenar(String::from("cache de dados"));
let i_num = cache.armazenar(42u64);
let i_vec = cache.armazenar(vec![1, 2, 3]);
// Recuperar como referencia tipada
if let Some(texto) = cache.recuperar::<String>(i_str) {
println!("String: {texto}");
}
if let Some(num) = cache.recuperar::<u64>(i_num) {
println!("Numero: {num}");
}
if let Some(lista) = cache.recuperar::<Vec<i32>>(i_vec) {
println!("Vec: {lista:?}");
}
// Tipo errado retorna None — sem panic
assert!(cache.recuperar::<bool>(i_str).is_none());
}
Padroes Comuns
Padrao as_any() em Traits
Quando voce tem uma trait customizada e precisa fazer downcast, inclua um metodo as_any():
use std::any::Any;
trait Componente: Any {
fn atualizar(&mut self);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
Este padrao e necessario porque dyn MinhaTraitCustomizada nao pode ser convertido diretamente para dyn Any.
Restricao ‘static
Any requer que o tipo seja 'static, ou seja, nao contenha referencias com lifetimes nao-estaticos. Isso significa que voce nao pode usar Any com tipos como &'a str (onde 'a nao e 'static). Use String ou tipos owned.
TypeId como Chave de HashMap
TypeId implementa Hash e Eq, sendo perfeito como chave para mapas tipo-valor:
use std::any::TypeId;
use std::collections::HashMap;
type TypeMap = HashMap<TypeId, Box<dyn std::any::Any>>;
Quando Usar (e Quando Nao Usar)
Use std::any quando:
- Precisar de colecoes heterogeneas (valores de tipos diferentes)
- Implementando sistemas de plugins ou extensoes
- Criando registros de servicos ou injecao de dependencia
- Precisar comparar tipos em runtime
Nao use std::any quando:
- Enums resolverem o problema (preferivel a type erasure)
- Generics forem suficientes (resolucao em compile-time e melhor)
- Trait objects normais (
dyn Trait) atenderem a necessidade - Estiver tentando emular heranca de classes OOP
Dica de performance: TypeId::of::<T>() e uma operacao const — e resolvida em tempo de compilacao. downcast_ref envolve apenas uma comparacao de TypeId, sendo extremamente rapida (tempo constante).
Limitacao importante: Any so funciona com tipos 'static. Se voce precisa de type erasure com lifetimes arbitrarios, considere usar trait objects com generics ou crates como typemap.
Veja Tambem
- Trait Objects vs Generics — quando usar cada abordagem
- Modulo std::marker — traits de marcacao como
SizedeSend - Modulo std::convert — conversoes seguras entre tipos
- O Prelude do Rust — traits importadas automaticamente
- Documentacao oficial — std::any