L'Enum Option revisitée
Fait partie de la section Object Oriented Programming du Journey Rust de Coddy — leçon 17 sur 61.
Vous avez déjà rencontré Option<T> en Rust — c'est l'énumération standard pour représenter des valeurs qui peuvent ou non exister. Examinons-le maintenant sous l'angle de la POO : Option n'est pas seulement une énumération, c'est un type doté de puissantes méthodes intégrées qui vous permettent de gérer les valeurs nullables de manière sûre et élégante.
Au lieu d'utiliser le filtrage par motif à chaque fois, vous pouvez utiliser des méthodes directement sur les valeurs Option. La méthode unwrap_or renvoie la valeur interne si elle existe, ou une valeur par défaut que vous fournissez :
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
La méthode map transforme la valeur interne si elle existe, laissant None inchangé. Cela vous permet d'enchaîner les opérations sans vérifier constamment la présence de None :
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Ces méthodes incarnent les principes de la POO — le type Option encapsule le concept de « peut-être une valeur » et fournit une interface propre pour travailler avec celle-ci. Vous appelez des méthodes sur l'objet plutôt que d'écrire une logique externe pour l'inspecter.
Défi
FacileConstruisons un système de profil utilisateur qui gère élégamment les données optionnelles en utilisant les méthodes de Option. Les utilisateurs peuvent avoir un pseudonyme défini ou non — votre système gérera les deux cas de manière fluide sans utiliser de filtrage par motif (matching) explicite.
Vous allez créer deux fichiers pour organiser votre code :
profile.rs: Définissez une structure publiqueProfileavec deux champs :username(String) etnickname(Option<String>). Implémentez :- Un constructeur
newqui prend un nom d'utilisateur et un pseudonyme optionnel - Une méthode
display_namequi retourne le pseudonyme s'il existe, ou le nom d'utilisateur par défaut — utilisezunwrap_orpour gérer cela - Une méthode
formatted_nicknamequi retourne uneOption<String>avec le pseudonyme entouré de crochets (comme"[CoolNick]") s'il existe, ouNones'il n'y a pas de pseudonyme — utilisezmappour transformer la valeur
- Un constructeur
main.rs: Importez votre module de profil et créez deux profils — un avec un pseudonyme et un sans. Démontrez les deux méthodes sur chaque profil pour montrer comment les méthodes deOptiongèrent différemment les valeurs présentes et absentes.
Pour la méthode formatted_nickname, utilisez le formatage de débogage ({:?}) lors de l'affichage puisqu'elle retourne une Option.
Votre sortie doit suivre exactement ce format, affichant les deux profils :
Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}Par exemple, avec un profil pour l'utilisateur "alice" avec le pseudonyme "Ace", et un autre pour l'utilisateur "bob" sans pseudonyme, la sortie serait :
Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: NoneVous recevrez trois entrées : le premier nom d'utilisateur, le pseudonyme du premier utilisateur, et le second nom d'utilisateur (qui n'a pas de pseudonyme).
Aide-mémoire
Le type Option<T> fournit des méthodes pour travailler avec des valeurs optionnelles sans correspondance explicite.
La méthode unwrap_or renvoie la valeur interne si elle existe, ou une valeur par défaut si elle est None :
let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name); // Alice
let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback); // Guest
La méthode map transforme la valeur interne si elle existe, en laissant None inchangé :
let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing); // None
Ces méthodes permettent de chaîner des opérations sur des valeurs Option sans avoir à vérifier constamment la présence de None.
Essayez vous-même
mod profile;
use profile::Profile;
fn main() {
// Lire les entrées
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let username1 = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let nickname1 = input2.trim().to_string();
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let username2 = input3.trim().to_string();
// TODO: Créer le premier profil avec username1 et nickname1 (Some)
// TODO: Créer le second profil avec username2 et aucun pseudonyme (None)
// TODO: Afficher display_name et formatted_nickname pour le premier profil
// Utiliser {:?} pour formatted_nickname puisqu'il retourne une Option
// TODO: Afficher display_name et formatted_nickname pour le second profil
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Méthodes et comportement
Introduction aux blocs d'implémentationLe paramètre SelfMéthodes mutablesFonctions associéesBlocs d'implémentation multiplesChaînage de méthodesRécapitulatif - Actions sur le rectangle4Projet : Animal virtuel
Définir l'animalNourrir l'animal7Traits standards
Le trait DebugLe trait DisplayClone et CopyLes traits d'égalitéRécapitulatif - Point affichable10Projet : Système de documents
Le trait DrawComposant texte2Encapsulation et modules
Bases des modulesLe mot-clé publicChamps privésGettersSettersRécapitulatif - Casier sécurisé5Généricité
Structures génériquesMéthodes génériquesPlusieurs types génériquesFonctions génériquesRécapitulatif - Point de coordonnées8Les Traits comme contraintes
Syntaxe des contraintes de traitContraintes multiplesLa clause whereRetourner des types avec des traitsRécapitulatif - Afficheur générique11Design Patterns en Rust
Le pattern NewtypeCompositionLe trait DropFrom et IntoRécapitulatif - Mock de Smart Pointer3Enums avancées
Enums avec donnéesMéthodes sur les EnumsMatching des variantes de donnéesL'Enum Option revisitéeRécapitulatif - Enum Shape6Définition des Traits
Qu'est-ce qu'un Trait ?Implémenter des TraitsImplémentations par défautRedéfinir les comportements par défautTraits avec paramètresRécapitulatif - Lecteur multimédia