Conversion de types en Rust
Rust ne convertit jamais un type en un autre à votre place : si vous mélangez un i32 et un f64 dans la même expression, le code ne compile pas. Vous devez convertir explicitement. C’est voulu : dans d’autres langages, les conversions silencieuses sont une source fréquente de bugs.
Convertir entre types numériques avec as
Section intitulée « Convertir entre types numériques avec as »Utilisez le mot-clé as pour convertir (on dit aussi « caster ») un type numérique en un autre :
fn main() { let x: i32 = 10; let y: f64 = x as f64; // i32 -> f64
println!("{y}");}C’est plus important qu’il n’y paraît. Divisez deux entiers sans conversion et vous obtiendrez une division entière au lieu du résultat décimal que vous attendiez sans doute :
fn main() { let a = 10; let b = 3;
println!("{}", a / b); // 3 (division entière) println!("{}", a as f64 / b as f64); // 3.3333333333333335}Convertir vers un type plus petit peut perdre des données
Section intitulée « Convertir vers un type plus petit peut perdre des données »Convertir d’un grand type vers un plus petit (par exemple de i32 vers u8) n’échoue pas : la valeur est simplement tronquée, ce qui peut donner un résultat surprenant :
fn main() { let big: i32 = 300; let small = big as u8; // u8 ne contient que 0 à 255 println!("{small}"); // 44, pas 300 : la valeur a « fait le tour »}Convertir des flottants, des caractères et des booléens
Section intitulée « Convertir des flottants, des caractères et des booléens »as fonctionne avec quelques types de plus que les entiers. Chacun a une règle à connaître :
fn main() { println!("{}", 3.99_f64 as i32); // 3 : les décimales sont coupées println!("{}", -3.99_f64 as i32); // -3 : coupé vers zéro, pas vers le bas println!("{}", 3.99_f64.round() as i32); // 4 : arrondissez d'abord si c'est ce que vous voulez println!("{}", 300.0_f64 as u8); // 255 : trop grand, donc bloqué au maximum println!("{}", -5.0_f64 as u8); // 0 : trop petit, donc bloqué au minimum println!("{}", 'A' as u32); // 65 : le numéro de code d'un char println!("{}", 97_u8 as char); // a : et dans l'autre sens println!("{}", true as i32); // 1 (false donnerait 0)}Sortie :
3-34255065a1À retenir :
- D’un flottant vers un entier, tout ce qui suit la virgule est supprimé. Il n’y a pas d’arrondi. Appelez
.round()d’abord si vous voulez l’entier le plus proche. - Les flottants hors limites sont ramenés à la valeur la plus proche qui rentre (
300.0devient255dans unu8). C’est différent des entiers, qui « font le tour » (300est devenu44plus haut). - Les caractères cachent un nombre (leur point de code Unicode).
'A' as u32donne65. Seul unu8peut être reconverti directement enchar; pour des nombres plus grands, on utilisechar::from_u32, car tous les nombres ne correspondent pas à un caractère valide. - Les booléens deviennent
1ou0. L’inverse ne marche pas :1 as boolne compile pas. Utilisez plutôt une comparaison commen != 0.
Transformer une chaîne en nombre
Section intitulée « Transformer une chaîne en nombre »La conversion avec as ne fonctionne qu’entre types numériques. Pour transformer du texte en nombre (par exemple quelque chose tapé par l’utilisateur), utilisez .parse() :
fn main() { let text = "42"; let number: i32 = text.parse().expect("nombre invalide");
println!("{}", number + 8);}Sortie :
50.parse() peut échouer (et si le texte n’est pas du tout un nombre ?), il renvoie donc un Result plutôt que le nombre directement. Nous n’avons pas encore vu Result : pour l’instant, .expect("message") est une façon simple de dire « plante avec ce message en cas d’échec ». Vous verrez la bonne manière de gérer ce cas sur la page Result.
Transformer un nombre en chaîne
Section intitulée « Transformer un nombre en chaîne »Dans l’autre sens, c’est plus simple : presque tous les types peuvent devenir une String avec .to_string() :
fn main() { let number = 42; let text = number.to_string(); println!("{}", text + "!");}Exercices
Section intitulée « Exercices »1. Moyenne des notes
Section intitulée « 1. Moyenne des notes »Stockez trois notes d’examen sous forme de i32 : 7, 8 et 10. Affichez leur somme, puis leur moyenne avec deux décimales. Indice : si vous divisez deux entiers, vous obtenez 8 et non 8.33 ; convertissez donc d’abord la somme en f64. {:.2} affiche deux décimales.
Voir la solution
fn main() { let a: i32 = 7; let b: i32 = 8; let c: i32 = 10;
let sum = a + b + c; let average = sum as f64 / 3.0; // 25.0 / 3.0, et non 25 / 3
println!("somme : {sum}"); println!("moyenne : {:.2}", average);}Sortie :
somme : 25moyenne : 8.33Remarquez que Rust affiche les décimales avec un point (8.33), jamais avec une virgule.
2. Un prix sous forme de texte
Section intitulée « 2. Un prix sous forme de texte »Une boutique vous donne le prix sous forme de texte, "4.50", et la quantité sous forme de i32, 3. Transformez le texte en f64, multipliez par la quantité et affichez Total : 13.5. Indice : il vous faut les deux outils de cette page : .parse() pour le texte et as f64 pour la quantité.
Voir la solution
fn main() { let price_text = "4.50"; let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("prix invalide"); let total = price * quantity as f64; // f64 * f64
println!("Total : {total}");}Sortie :
Total : 13.5Sans as f64, Rust refuserait de multiplier un f64 par un i32, car il ne mélange jamais les types numériques à votre place. Notez aussi que .parse() attend un point : "4,50" échouerait.
Récapitulatif
Section intitulée « Récapitulatif »- Rust ne convertit jamais les types automatiquement : vous convertissez toujours explicitement.
- Utilisez
aspour convertir entre types numériques :x as f64,x as u8. - Convertir vers un type plus petit tronque sans prévenir : il n’y a pas d’erreur, alors soyez prudent.
- D’un flottant vers un entier, les décimales sont coupées (utilisez
.round()d’abord pour arrondir).charetboolpeuvent aussi être convertis en nombres. - Utilisez
.parse()pour transformer du texte en nombre, et.to_string()pour l’inverse.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Que se passe-t-il quand on divise deux entiers comme 10 / 3 en Rust, sans conversion ?
2. Que se passe-t-il quand on convertit 300_i32 as u8 ?
3. Que renvoie .parse() quand on convertit une chaîne en nombre, sachant que la conversion peut échouer ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Casting — Rust by Example (en anglais) : de nombreuses conversions avec
as, exécutables et commentées pour montrer exactement ce que chacune produit. - Type cast expressions — The Rust Reference (en anglais) : le tableau complet des conversions autorisées et la règle précise de chacune.
- To and from Strings — Rust by Example (en anglais) :
.to_string()et.parse()un peu plus en détail. TryFrom— documentation de la bibliothèque standard (en anglais) : la conversion vérifiée (u8::try_from(...)) à utiliser quand une valeur risque de ne pas rentrer.- Numeric Types — The Rust Reference (en anglais) : la taille et la plage de chaque type entier et flottant.