Introduction : pourquoi en Rust on ne peut pas simplement additionner un nombre et une chaîne ?
\n\nSi vous venez de Python ou JavaScript, vous avez l'habitude d'écrire quelque chose comme str(42) ou 42 + "". En Rust, cette astuce ne fonctionne pas. Le compilateur demande strictement : "Quel type veux-tu obtenir ? Integer ? String ? Décide-toi !"
La question "How do I convert from an integer to a string?" est l'une des plus populaires sur Stack Overflow dans la section Rust. Des milliers de développeurs se cassent la tête à essayer de comprendre pourquoi leur code ne compile pas lorsqu'ils tentent d'écrire :
\n\nlet number = 42;\nlet text = number; // Erreur : expected `&str`, found `i32`\n\nAujourd'hui, nous allons regarder sous le capot de cette conversion. Vous apprendrez non seulement comment le faire, mais aussi comment cela fonctionne au niveau des traits, des allocations et même de l'assembleur. C'est parti !
\n\nLa méthode la plus simple : la méthode to_string()
\n\nEn Rust, comme dans la plupart des langages modernes, les types entiers possèdent une méthode to_string(). Elle semble trompeusement simple :
fn main() {\n let num: i32 = 255;\n let s: String = num.to_string();\n println!("{}", s); // Affiche : "255"\n}\n\nCela fonctionne pour tous les types entiers : i8, u32, i64, usize, etc.
Que se passe-t-il sous le capot ?
\n\nLa méthode to_string() n'est pas une magie du compilateur. Elle est implémentée via le trait Display. Dans la bibliothèque standard de Rust, il existe une implémentation :
// Version simplifiée de la bibliothèque standard\nimpl<T: fmt::Display> ToString for T {\n #[inline]\n fn to_string(&self) -> String {\n use fmt::Write;\n let mut buf = String::new();\n // Ici, la méthode fmt() du trait Display est appelée\n let _ = buf.write_fmt(format_args!("{}", self));\n buf.shrink_to_fit(); // Optimisation mémoire\n buf\n }\n}\n\nPoint clé : to_string() crée une nouvelle String sur le tas (heap). Chaque appel est une allocation mémoire. Pour des centaines de milliers de conversions, cela peut devenir un goulot d'étranglement en termes de performances.
Alternative : la macro format!
\n\nLa deuxième méthode populaire est la macro format!. Elle fonctionne comme println!, mais au lieu d'afficher dans la console, elle retourne une String :
fn main() {\n let num = 1024;\n let s = format!("{}", num);\n println!("Longueur de la chaîne : {}", s.len()); // 4\n}\n\nLa différence entre to_string() et format! est minime. to_string() appelle en interne format_args! et write_fmt. En substance, c'est la même chose. Le choix est une question de style et de lisibilité.
Quand utiliser format! ?
\n- \n
- Lorsqu'il faut insérer un nombre au milieu d'une chaîne :
format!("Nombre : {}", num)\n - Lorsque le format est complexe (hexadécimal, avec séparateurs) :
format!("{:#x}", 255)→ "0xff" \n - Lorsque l'expressivité du code est importante \n
Trait Display vs ToString : lequel est plus rapide ?
\n\nSi vous écrivez une bibliothèque ou du code à forte charge, utilisez directement le trait Display via write! dans un buffer. Cela permet de réutiliser la mémoire déjà allouée :
use std::fmt::Write;\n\nfn main() {\n let num = 42;\n let mut buffer = String::with_capacity(20); // Allouer la mémoire à l'avance\n\n // Écrire le nombre dans le buffer\n write!(buffer, "{}", num).unwrap();\n println!("{}", buffer); // "42"\n\n // Vider et réutiliser\n buffer.clear();\n write!(buffer, "{}", 256).unwrap();\n println!("{}", buffer); // "256"\n}\n\nCette approche offre un gain de performances allant jusqu'à 2-3x dans les boucles où il faut convertir plusieurs fois des nombres en chaînes, car l'allocation mémoire se