Como exibir o tipo de uma variável em Rust e não enlouquecer
\n\nVocê já escreveu código em Rust e de repente parou com a pergunta: "Qual é o tipo desta variável?" Se sim — você não está sozinho. A pergunta "How do I print the type of a variable in Rust" no Stack Overflow acumulou milhares de visualizações, e não é por acaso. Rust é uma linguagem com um sistema de tipos rigoroso, mas inteligente, e às vezes o compilador sabe mais do que nós.
\n\nNeste artigo, vamos explorar três maneiras de exibir o tipo de uma variável: desde uma técnica simples de depuração até uma macro profissional. Você aprenderá quando usar cada método e deixará de adivinhar que bicho está escondido atrás de let x = ....
Por que o Rust não permite simplesmente imprimir o tipo?
\n\nEm linguagens como Python ou JavaScript, você pode escrever print(type(x)) — e pronto. No Rust, esse luxo não existe por dois motivos:
- \n
- Tipagem estática: os tipos são conhecidos em tempo de compilação, mas nem sempre são óbvios para o programador (especialmente ao trabalhar com closures ou genéricos complexos). \n
- Ausência de reflexão: Rust não armazena informações sobre tipos em tempo de execução por padrão — isso sobrecarregaria o desempenho. \n
Mas os desenvolvedores do Rust nos deixaram algumas brechas. Vamos dar uma olhada nelas.
\n\nMétodo 1: std::any::type_name — o mais simples (mas não ideal)
\n\nA biblioteca padrão do Rust fornece a função std::any::type_name. Ela retorna uma string com o nome do tipo. Veja como funciona:
use std::any::type_name;\n\nfn print_type<T>(_: &T) {\n println!("{}", type_name::<T>());\n}\n\nfn main() {\n let x = 42;\n let y = "hello";\n let z = vec![1, 2, 3];\n \n print_type(&x); // i32\n print_type(&y); // &str\n print_type(&z); // Vec<i32>\n}\n\nVantagens:
\n- \n
- Mínimo de código — funciona de imediato. \n
- Adequado para depuração e verificações rápidas. \n
Desvantagens:
\n- \n
- O resultado depende do compilador: em diferentes versões do Rust ou em diferentes plataformas, a string pode variar. \n
- Não pode ser usado em lógica de produção onde é necessário tomar decisões com base no tipo. \n
Exemplo prático: depuração de uma expressão complexa
\n\nImagine que você está trabalhando com iteradores e encadeamento de métodos. Qual será o tipo final?
\n\nuse std::any::type_name;\n\nfn main() {\n let data = vec![1, 2, 3, 4, 5];\n let result = data.iter()\n .filter(|x| *x % 2 == 0)\n .map(|x| x * 2)\n .collect::<Vec<_>>();\n \n // Quer saber o tipo antes do collect?\n let intermediate = data.iter()\n .filter(|x| *x % 2 == 0)\n .map(|x| x * 2);\n \n println!("Tipo do intermediate: {}", type_name::<decltype!(intermediate)>()); \n // Opa, assim não funciona — é preciso passar uma referência\n println!("Tipo do intermediate: {}", type_name_of_val(&intermediate));\n}\n\nfn type_name_of_val<T>(_: &T) -> &str {\n type_name::<T>()\n}\n\nEste truque vai te salvar quando o compilador emitir um erro e você não entender que tipo ele está esperando.
\n\nMétodo 2: Usar erros do compilador — um truque esperto
\n\nÀs vezes, a melhor maneira de saber o tipo é forçar o compilador a revelá-lo. Existe um truque antigo, mas eficaz: atribuir a variável a um tipo inexistente.
\n\nfn main() {\n let x = 42;\n let y: () = x; // Erro! expected (), found i32\n}\n\nO compilador exibirá algo como:
\nerror[E0308]: mismatched types\n --> src/main.rs:3:18\n |\n3 | let y: () = x;\n | -- ^ expected `()`, found `i32`\n | |\n | expected due to this\n\nVantagens:
\n- \n
- Não requer imports. \n
- Funciona até mesmo em ambientes sem std (no_st