Comment afficher le type d'une variable en Rust sans perdre la tête
\n\nAvez-vous déjà écrit du code en Rust et soudainement figé avec la question : "Mais quel est le type de cette variable ?" Si oui — vous n'êtes pas seul. La question "How do I print the type of a variable in Rust" sur Stack Overflow a recueilli des milliers de vues, et ce n'est pas un hasard. Rust est un langage avec un système de types strict mais intelligent, et parfois le compilateur en sait plus que nous.
\n\nDans cet article, nous allons explorer trois façons d'afficher le type d'une variable : de la simple astuce de débogage à la macro professionnelle. Vous apprendrez quand utiliser chaque méthode et cesserez de deviner quelle bête se cache derrière let x = ....
Pourquoi Rust ne permet-il pas simplement d'imprimer le type ?
\n\nDans des langages comme Python ou JavaScript, vous pouvez écrire print(type(x)) — et voilà. Dans Rust, ce luxe n'existe pas pour deux raisons :
- \n
- Typage statique : les types sont connus à la compilation, mais ne sont pas toujours évidents pour le programmeur (surtout avec les closures ou les génériques complexes). \n
- Absence de réflexion : Rust ne stocke pas les informations de type à l'exécution par défaut — cela alourdirait les performances. \n
Mais les développeurs de Rust nous ont laissé quelques astuces. Regardons-les.
\n\nMéthode 1 : std::any::type_name — la plus simple (mais pas idéale)
\n\nLa bibliothèque standard de Rust fournit la fonction std::any::type_name. Elle renvoie une chaîne avec le nom du type. Voici comment cela fonctionne :
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\nAvantages :
\n- \n
- Minimum de code — fonctionne directement. \n
- Adapté au débogage et aux vérifications rapides. \n
Inconvénients :
\n- \n
- Le résultat dépend du compilateur : dans différentes versions de Rust ou sur différentes plateformes, la chaîne peut varier. \n
- Ne peut pas être utilisé dans une logique de production où des décisions doivent être prises en fonction du type. \n
Exemple concret : débogage d'une expression complexe
\n\nImaginez que vous travaillez avec des itérateurs et des chaînes de méthodes. Quel type obtiendrez-vous à la fin ?
\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 // Nous voulons connaître le type avant collect ?\n let intermediate = data.iter()\n .filter(|x| *x % 2 == 0)\n .map(|x| x * 2);\n \n println!("Type de intermediate : {}", type_name::<decltype!(intermediate)>()); \n // Oups, ça ne marchera pas — il faut passer une référence\n println!("Type de intermediate : {}", type_name_of_val(&intermediate));\n}\n\nfn type_name_of_val<T>(_: &T) -> &str {\n type_name::<T>()\n}\n\nCette astuce vous sauvera lorsque le compilateur génère une erreur et que vous ne comprenez pas quel type il attend.
\n\nMéthode 2 : Utiliser les erreurs du compilateur — une astuce maline
\n\nParfois, la meilleure façon de connaître le type est de forcer le compilateur à le révéler lui-même. Il existe une vieille astuce qui fonctionne toujours : assigner la variable à un type inexistant.
\n\nfn main() {\n let x = 42;\n let y: () = x; // Erreur ! expected (), found i32\n}\n\nLe compilateur affichera quelque chose comme :
\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\nAvantages :
\n- \n
- Ne nécessite pas d'imports. \n
- Fonctionne même dans des environnements sans std (no_st