L'Enum Option revisitée
Fait partie de la section Programmation Orientée Objet 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 permettant de représenter des valeurs qui peuvent exister ou non. Examinons-la maintenant sous l’angle de la POO : Option n’est pas seulement une énumération, c’est un type doté de méthodes intégrées puissantes qui vous permettent de gérer les valeurs nullables de manière sûre et élégante.
Au lieu d’effectuer une correspondance à chaque fois, vous pouvez utiliser directement des méthodes 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, en laissant None inchangé. Cela vous permet d’enchaîner les opérations sans devoir 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 claire pour travailler avec celui-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 profils utilisateur qui gère élégamment les données facultatives à l’aide des méthodes de Option. Les utilisateurs peuvent avoir un pseudonyme défini, ou ne pas en avoir. Votre système gérera les deux cas avec élégance, sans correspondance 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 facultatif - Une méthode
display_namequi renvoie le pseudonyme s’il existe, ou le nom d’utilisateur comme solution de repli : utilisezunwrap_orpour gérer cela - Une méthode
formatted_nicknamequi renvoie unOption<String>contenant le pseudonyme entre 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 pseudonyme. Démontrez les deux méthodes sur chaque profil afin de 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 renvoie un Option.
Votre sortie doit respecter exactement le format suivant, en affichant les deux profils :
Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}Par exemple, avec un profil pour le nom d’utilisateur "alice" et le pseudonyme "Ace", et un autre pour le nom d’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 deuxième nom d’utilisateur (qui n’a pas de pseudonyme).
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: Create second profile with username2 and no nickname (None)
// TODO: Print display_name and formatted_nickname for first profile
// Use {:?} for formatted_nickname since it returns an Option
// TODO: Print display_name and formatted_nickname for second profile
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
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édiaEntraînez-vous par vous-même : Compilateur Rust en ligne