Une structure de contrôle concise : if let
La syntaxe if let
vous permet de combiner if
et let
afin de gérer les
valeurs qui correspondent à un motif donné, tout en ignorant les autres.
Imaginons le programme dans l'encart 6-6 qui fait un match
sur la valeur
Option<u8>
de la variable une_valeur_u8
mais n'a besoin d'exécuter du code que
si la valeur est la variante Some
.
fn main() { let une_valeur_u8 = Some(3u8); match une_valeur_u8 { Some(max) => println!("Le maximum est réglé sur {}", max), _ => (), } }
Si la valeur est un Some
, nous affichons la valeur dans la variante Some
en
associant la valeur à la variable max
dans le motif. Nous ne voulons rien
faire avec la valeur None
. Pour satisfaire l'expression match
, nous devons
ajouter _ => ()
après avoir géré une seule variante, ce qui est du code
inutile.
À la place, nous pourrions écrire le même programme de manière plus concise en
utilisant if let
. Le code suivant se comporte comme le match
de l'encart
6-6 :
fn main() { let une_valeur_u8 = Some(3u8); if let Some(max) = une_valeur_u8 { println!("Le maximum est réglé sur {}", max); } }
La syntaxe if let
prend un motif et une expression séparés par un signe égal.
Elle fonctionne de la même manière qu'un match
où l'expression est donnée au
match
et où le motif est sa première branche. Dans ce cas, le motif est
Some(max)
, et le max
est associé à la valeur dans le Some
. Nous pouvons
ensuite utiliser max
dans le corps du bloc if let
de la même manière que
nous avons utilisé max
dans la branche correspondante au match
. Le code dans
le bloc if let
n'est pas exécuté si la valeur ne correspond pas au motif.
Utiliser if let
permet d'écrire moins de code, et de moins l'indenter.
Cependant, vous perdez la vérification de l'exhaustivité qu'assure le match
.
Choisir entre match
et if let
dépend de la situation : à vous de choisir
s'il vaut mieux être concis ou appliquer une vérification exhaustive.
Autrement dit, vous pouvez considérer le if let
comme du sucre syntaxique pour
un match
qui exécute du code uniquement quand la valeur correspond à un motif
donné et ignore toutes les autres valeurs.
Nous pouvons joindre un else
à un if let
. Le bloc de code qui va dans le
else
est le même que le bloc de code qui va dans le cas _
avec l'expression
match
. Souvenez-vous de la définition de l'énumération PieceUs
de l'encart
6-4, où la variante Quarter
stockait aussi une valeur EtatUs
. Si nous
voulions compter toutes les pièces qui ne sont pas des quarters que nous
voyons passer, tout en affichant l'État des quarters, nous pourrions le faire
avec une expression match
comme ceci :
#[derive(Debug)] enum EtatUs { Alabama, Alaska, // -- partie masquée ici -- } enum PieceUs { Penny, Nickel, Dime, Quarter(EtatUs), } fn main() { let piece = PieceUs::Penny; let mut compteur = 0; match piece { PieceUs::Quarter(etat) => println!("Il s'agit d'un quarter de l'État de {:?} !", etat), _ => compteur += 1, } }
Ou nous pourrions utiliser une expression if let
/else
comme ceci :
#[derive(Debug)] enum EtatUs { Alabama, Alaska, // -- partie masquée ici -- } enum PieceUs { Penny, Nickel, Dime, Quarter(EtatUs), } fn main() { let piece = PieceUs::Penny; let mut compteur = 0; if let PieceUs::Quarter(etat) = piece { println!("Il s'agit d'un quarter de l'État de {:?} !", etat); } else { compteur += 1; } }
Si vous trouvez que votre programme est alourdi par l'utilisation d'un match
,
souvenez-vous que if let
est aussi présent dans votre boite à outils Rust.
Résumé
Nous avons désormais appris comment utiliser les énumérations pour créer des
types personnalisés qui peuvent faire partie d'un jeu de valeurs recensées. Nous
avons montré comment le type Option<T>
de la bibliothèque standard vous aide
à utiliser le système de types pour éviter les erreurs. Lorsque les valeurs
d'énumération contiennent des données, vous pouvez utiliser match
ou if let
pour extraire et utiliser ces valeurs, à choisir en fonction du nombre de cas
que vous voulez gérer.
Vos programmes Rust peuvent maintenant décrire des concepts métier à l'aide de structures et d'énumérations. Créer des types personnalisés à utiliser dans votre API assure la sécurité des types : le compilateur s'assurera que vos fonctions ne reçoivent que des valeurs du type attendu.
Afin de fournir une API bien organisée, simple à utiliser et qui n'expose que ce dont vos utilisateurs auront besoin, découvrons maintenant les modules de Rust.