backend / rust
Types de base et tuples
Explication
Ce que vous allez apprendre
- Choisir la bonne taille d'entier signé ou non signé selon le besoin
- Comprendre la différence de comportement entre overflow en debug et en release
- Utiliser
checked_add/wrapping_addpour un comportement d'overflow explicite et contrôlé - Regrouper des types hétérogènes dans un tuple et le déstructurer
- Convertir entre types numériques avec
as, et connaître son risque de troncature silencieuse
Dans quel contexte ?
Un développeur teste sa fonction de calcul de score en local et tout fonctionne parfaitement. Une fois déployée en mode release (production), un dépassement de capacité (overflow) sur un compteur u8 produit silencieusement une valeur incohérente au lieu de planter comme en développement — un piège qui vient précisément de la différence de comportement entre les deux modes de compilation que cette leçon détaille.
D'abord, il faut choisir consciemment la taille d'un entier
Rust distingue précisément huit tailles d'entiers signés (i8 à i128) et huit tailles non signées (u8 à u128), plus deux tailles dépendantes de la plateforme (isize, usize). Ce dernier type, usize, est particulièrement important : c'est le type utilisé pour indexer les collections, car sa taille correspond exactement à celle d'une adresse mémoire sur la machine cible.
| Catégorie | Exemples | Usage typique |
|---|---|---|
| Entiers signés | i8, i32, i64 | Valeurs pouvant être négatives |
| Entiers non signés | u8, u32, u64 | Compteurs, tailles, valeurs jamais négatives |
| Taille dépendante | usize, isize | Indexation de collections, taille mémoire |
| Flottants | f32, f64 | Décimaux (f64 par défaut) |
Une fois ce choix fait, il faut comprendre un piège de comportement peu connu
Un dépassement de capacité (par exemple additionner 10 à un u8 qui vaut déjà 250, alors que le maximum est 255) déclenche un panic immédiat en mode debug, mais "boucle" silencieusement (wrap) en mode release — deux comportements radicalement différents pour le même code source.
Prérequis
Il faut être à l'aise avec les variables et la mutabilité (leçon précédente) pour comprendre les exemples de cette leçon.
Il reste une solution pour ne jamais dépendre de ce comportement implicite
checked_add retourne un Option<T> : Some(resultat) si tout va bien, None en cas de dépassement — une gestion explicite et sûre, quel que soit le mode de compilation. wrapping_add, à l'inverse, choisit délibérément le comportement de "wrap" (retour à zéro) partout, sans jamais paniquer.
Piège dangereux
Ne jamais s'appuyer sur le comportement par défaut d'un dépassement d'entier (+, -, * classiques) dans du code destiné à la production, car ce comportement change selon le profil de compilation. Utilise systématiquement checked_* quand un dépassement est possible et doit être détecté, ou wrapping_*/saturating_* quand un comportement précis est voulu intentionnellement.
Enfin, les tuples regroupent des types différents en une seule valeur
let point: (i32, i32, i32) = (10, 20, 30); illustre un tuple à taille fixe, dont chaque élément peut avoir un type différent. La destructuration (let (x, y, z) = point;) extrait chaque valeur dans une variable nommée, bien plus lisible que l'accès positionnel point.0.
Bonne pratique
Utilise as pour une conversion numérique uniquement quand tu es certain que la valeur source rentre dans le type cible. Dans tous les autres cas, préfère TryFrom/TryInto, qui retournent un Result explicite plutôt que de tronquer silencieusement une valeur trop grande, comme le fait as.
Maintenant que tu maîtrises les types de base, il est temps d'aborder le concept qui rend Rust unique parmi tous les langages grand public : l'ownership, sujet de la prochaine leçon.
Commandes & code
Types de base et tuples
Rust est statiquement typé, avec une inférence de type puissante.
fn main() {
// Entiers : tailles explicites, signes ou non
let a: i32 = -42; // entier signe 32 bits (defaut si non precise)
let b: u8 = 255; // entier non signe 8 bits
let c: i64 = 9_000_000_000;
let d: usize = 10; // taille dependante de la plateforme, utilise pour les index
// Flottants
let e: f64 = 3.14159; // defaut
let f: f32 = 2.71;
// Booleen et caractere (char = 4 octets, un scalaire Unicode)
let g: bool = true;
let h: char = 'é';
// Inference : le type est deduit du contexte
let i = 42; // i32 par defaut
let j = 3.14; // f64 par defaut
// Overflow : panique en mode debug, wrap en mode release (comportement different !)
let k: u8 = 250;
let l = k.wrapping_add(10); // wrap explicite et controle : 4
let m = k.checked_add(10); // retourne Option<u8> : None si overflow
println!("{} {:?}", l, m);
// Tuples : regrouper des types differents, taille fixe
let point: (i32, i32, i32) = (10, 20, 30);
let (x, y, z) = point; // destructuration
println!("{} {} {}", x, y, z);
println!("{}", point.0); // acces par index
// Tuple unitaire (unit type) : represente "rien", souvent le retour de fonctions
let rien: () = ();
// Conversion explicite avec `as` (peut tronquer silencieusement !)
let grand: i64 = 300;
let petit = grand as u8; // 44 : troncature, a utiliser avec prudence
println!("{} {} {} {} {} {} {}", a, b, c, d, e, f, g);
println!("{}", h);
println!("{} {}", i, j);
println!("{}", petit);
let _ = rien;
}| Catégorie | Types |
|---|---|
| Entiers signés | i8, i16, i32, i64, i128, isize |
| Entiers non signés | u8, u16, u32, u64, u128, usize |
| Flottants | f32, f64 |
| Autres | bool, char, () (unit) |
Résumé
usize/isizes'adaptent à l'architecture (souvent utilisés pour indexer des collections).- Un dépassement d'entier panique en mode debug, wrap silencieusement en release :
checked_*/wrapping_*rendent le comportement explicite. - Un tuple regroupe des types hétérogènes de taille fixe, destructurable avec
let (a, b) = t. asconvertit mais peut tronquer sans avertissement : préférerTryFromquand la sécurité compte.
Exercices pratiques
Mission : un compteur de stock qui ment en production
Objectif : Diagnostiquer un bug d'overflow silencieux specifique au mode release et le corriger avec les bons outils de conversion.
Contexte
Le service d'inventaire d'un entrepot utilise un u8 pour compter les articles d'un petit casier (max 255 attendu). En debug, tout fonctionne. Une fois deploye en --release, le compteur d'un casier tres actif se met soudainement a afficher des valeurs absurdes proches de zero apres avoir depasse 255 articles recus dans la journee, sans jamais planter ni logger d'erreur. Personne ne comprend pourquoi le bug n'apparaissait jamais en test local.