Option en Rust
Rust n’a pas de null. Si une valeur peut être absente, cela s’exprime directement dans le type, grâce à Option. Au début, cela ressemble à une formalité de plus, mais cela élimine toute une catégorie de bugs : la fameuse « référence nulle », responsable d’innombrables plantages dans d’autres langages.
À quoi ressemble Option
Section intitulée « À quoi ressemble Option »Option est une enum à deux variantes :
enum Option<T> { Some(T), // une valeur est présente None, // pas de valeur}En fait, vous l’avez déjà croisée : .get() sur un vecteur et .get() sur une HashMap renvoient tous les deux une Option.
Créer une Option
Section intitulée « Créer une Option »fn main() { let has_value: Option<i32> = Some(5); let no_value: Option<i32> = None;
println!("{:?}", has_value); println!("{:?}", no_value);}Sortie :
Some(5)NoneTraiter une Option avec match
Section intitulée « Traiter une Option avec match »La façon sûre de récupérer la valeur, c’est match. Rust vous oblige à traiter les deux cas, si bien que vous ne pouvez pas oublier par accident le cas « absent » :
fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("L'âge est {value}"), None => println!("Aucun âge indiqué"), }}Un exemple réaliste
Section intitulée « Un exemple réaliste »fn find_user(id: u32) -> Option<&'static str> { if id == 1 { Some("Alice") } else { None }}
fn main() { match find_user(1) { Some(name) => println!("Trouvé : {name}"), None => println!("Utilisateur introuvable"), }
match find_user(99) { Some(name) => println!("Trouvé : {name}"), None => println!("Utilisateur introuvable"), }}Sortie :
Trouvé : AliceUtilisateur introuvableAu lieu de renvoyer null ou de lever une exception quand l’utilisateur n’existe pas, c’est le type de retour de la fonction lui-même qui prévient chaque appelant : « il se peut que je ne trouve rien ». Et le compilateur ne le laissera pas ignorer cette possibilité.
Les raccourcis : unwrap_or et unwrap
Section intitulée « Les raccourcis : unwrap_or et unwrap »Dans les cas simples, un match complet peut sembler excessif. Voici deux raccourcis :
fn main() { let age: Option<i32> = None;
let value = age.unwrap_or(0); // valeur de repli : 0 s'il n'y a pas de valeur println!("{value}");}Il existe aussi .unwrap(), qui vous donne directement la valeur, mais qui panique (fait planter le programme) si c’est None :
let age: Option<i32> = Some(30);let value = age.unwrap(); // aucun problème ici, puisque age vaut SomeExercices
Section intitulée « Exercices »1. Le premier nombre pair
Section intitulée « 1. Le premier nombre pair »Écrivez fn first_even(numbers: &[i32]) -> Option<i32>, qui renvoie le premier nombre pair de la slice, ou None s’il n’y en a aucun. Affichez le résultat pour [3, 7, 8, 10] avec match, et pour [1, 3, 5] avec .unwrap_or(0). Indice : parcourez la slice et faites return Some(n) dès que n % 2 == 0 ; après la boucle, renvoyez None.
Voir la solution
fn first_even(numbers: &[i32]) -> Option<i32> { for &n in numbers { if n % 2 == 0 { return Some(n); } } None}
fn main() { match first_even(&[3, 7, 8, 10]) { Some(n) => println!("Premier nombre pair : {n}"), None => println!("Aucun nombre pair"), }
let fallback = first_even(&[1, 3, 5]).unwrap_or(0); println!("Avec valeur de repli : {fallback}");}Sortie :
Premier nombre pair : 8Avec valeur de repli : 02. Lire un réglage
Section intitulée « 2. Lire un réglage »Créez une HashMap de réglages qui contient seulement "volume", réglé à 80. Ensuite, parcourez les clés "volume" et "luminosité", et affichez la valeur si elle existe, ou un message « non défini » sinon. Indice : .get(key) sur une HashMap renvoie une Option : vous pouvez donc faire un match directement dessus.
Voir la solution
use std::collections::HashMap;
fn main() { let mut settings = HashMap::new(); settings.insert("volume", 80);
for key in ["volume", "luminosité"] { match settings.get(key) { Some(value) => println!("{key} = {value}"), None => println!("{key} : non défini"), } }}Sortie :
volume = 80luminosité : non définiRécapitulatif
Section intitulée « Récapitulatif »Option<T>représente une valeur qui peut être présente ou non : Rust n’a pas denull.Some(value)contient quelque chose ;Nonesignifie qu’il n’y a rien.matchest la façon sûre de traiter les deux cas ; le compilateur ne vous laissera pas en oublier un..unwrap_or(default)fournit une valeur de repli ;.unwrap()panique si la valeur estNone, alors utilisez-le avec modération.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Qu'utilise Rust à la place de null pour une valeur qui peut être absente ?
2. Que se passe-t-il quand vous appelez .unwrap() sur une valeur None ?
3. Que fait age.unwrap_or(0) quand age vaut None ?
4. Pourquoi match est-il considéré comme la façon sûre de récupérer la valeur d'une Option ?
5. Parmi les méthodes citées sur cette page, lesquelles renvoient déjà une Option ?
Score : 0 / 5
Pour aller plus loin
Section intitulée « Pour aller plus loin »- The Option Enum and Its Advantages Over Null Values — The Rust Book (en anglais) : l’explication officielle de pourquoi Rust utilise
Optionau lieu denull. std::option— documentation de la bibliothèque standard (en anglais) : une vue d’ensemble de tout ce qu’on peut faire avec uneOption, y compris des méthodes pratiques commemap,and_thenetis_some.- Option & unwrap — Rust by Example (en anglais) : de courts exemples exécutables de
match, deunwrapet du moment où une panic se produit. Option<T>— documentation std (en anglais) : la liste complète des méthodes, chacune avec un petit exemple à copier.