Tous les endroits où les motifs peuvent être utilisés

RustRustBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Bienvenue dans All the Places Patterns Can Be Used. Ce laboratoire est une partie du Rust Book. Vous pouvez pratiquer vos compétences Rust dans LabEx.

Dans ce laboratoire, nous allons explorer tous les endroits où les motifs peuvent être utilisés en Rust.

All the Places Patterns Can Be Used

Les motifs apparaissent dans de nombreux endroits en Rust, et vous les utilisez déjà beaucoup sans vous en rendre compte! Cette section traite de tous les endroits où les motifs sont valides.

match Arms

Comme discuté au chapitre 6, nous utilisons des motifs dans les bras des expressions match. Formellement, les expressions match sont définies par le mot-clé match, une valeur à comparer, et un ou plusieurs bras de correspondance qui consistent en un motif et une expression à exécuter si la valeur correspond au motif de ce bras, comme ceci :

match VALEUR {
    MOTIF => EXPRESSION,
    MOTIF => EXPRESSION,
    MOTIF => EXPRESSION,
}

Par exemple, voici l'expression match de la liste 6-5 qui compare une valeur Option<i32> dans la variable x :

match x {
    None => None,
    Some(i) => Some(i + 1),
}

Les motifs dans cette expression match sont None et Some(i) à gauche de chaque flèche.

Une exigence pour les expressions match est qu'elles doivent être exhaustives au sens où toutes les possibilités pour la valeur dans l'expression match doivent être prises en compte. Une manière de s'assurer d'avoir couvert toutes les possibilités est d'avoir un motif générique pour le dernier bras : par exemple, un nom de variable correspondant à n'importe quelle valeur ne peut jamais échouer et couvre donc tous les cas restants.

Le motif particulier _ correspond à tout, mais il ne se lie jamais à une variable, il est donc souvent utilisé dans le dernier bras de correspondance. Le motif _ peut être utile lorsque vous voulez ignorer toute valeur non spécifiée, par exemple. Nous aborderons le motif _ en détail dans "Ignoring Values in a Pattern".

Conditional if let Expressions

Au chapitre 6, nous avons discuté de la manière d'utiliser les expressions if let principalement comme un moyen plus court d'écrire l'équivalent d'un match qui ne correspond qu'à un seul cas. Facultativement, if let peut avoir un else correspondant contenant le code à exécuter si le motif dans if let ne correspond pas.

Le listing 18-1 montre qu'il est également possible de mélanger et d'associer des expressions if let, else if et else if let. Cela nous donne plus de flexibilité qu'une expression match dans laquelle nous ne pouvons exprimer qu'une seule valeur à comparer avec les motifs. De plus, Rust n'exige pas que les conditions dans une série de bras if let, else if et else if let soient liées les unes aux autres.

Le code du listing 18-1 détermine quelle couleur utiliser pour votre fond d'écran en fonction d'une série de vérifications pour plusieurs conditions. Pour cet exemple, nous avons créé des variables avec des valeurs codées en dur que pourrait recevoir un programme réel à partir d'une entrée utilisateur.

Nom de fichier : src/main.rs

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();

  1 if let Some(color) = favorite_color {
      2 println!(
            "Using your favorite, {color}, as the background"
        );
  3 } else if is_tuesday {
      4 println!("Tuesday is green day!");
  5 } else if let Ok(age) = age {
      6 if age > 30 {
          7 println!("Using purple as the background color");
        } else {
          8 println!("Using orange as the background color");
        }
  9 } else {
     10 println!("Using blue as the background color");
    }
}

Listing 18-1: Mixing if let, else if, else if let, and else

Si l'utilisateur spécifie une couleur favorite [1], cette couleur est utilisée comme fond d'écran [2]. Si aucune couleur favorite n'est spécifiée et que aujourd'hui est mardi [3], la couleur de fond est verte [4]. Sinon, si l'utilisateur spécifie son âge sous forme de chaîne de caractères et que nous pouvons le parser en tant que nombre avec succès [5], la couleur est soit violette [7] soit orange [8] selon la valeur du nombre [6]. Si aucune de ces conditions n'est applicable [9], la couleur de fond est bleue [10].

Cette structure conditionnelle nous permet de prendre en charge des exigences complexes. Avec les valeurs codées en dur que nous avons ici, cet exemple affichera Using purple as the background color.

Vous pouvez voir que if let peut également introduire des variables masquées de la même manière que les bras de match le peuvent : la ligne if let Ok(age) = age [5] introduit une nouvelle variable age masquée qui contient la valeur à l'intérieur de la variante Ok. Cela signifie que nous devons placer la condition if age > 30 [6] à l'intérieur de ce bloc : nous ne pouvons pas combiner ces deux conditions en if let Ok(age) = age && age > 30. L'age masqué que nous voulons comparer à 30 n'est pas valide jusqu'au début du nouveau scope avec les accolades.

Le inconvénient d'utiliser des expressions if let est que le compilateur ne vérifie pas l'exhaustivité, tandis qu'avec les expressions match il le fait. Si nous omettions le dernier bloc else [9] et donc manquions de traiter certains cas, le compilateur ne nous alerterait pas sur le possible bogue logique.

while let Conditional Loops

De construction similaire à if let, la boucle conditionnelle while let permet à une boucle while de s'exécuter aussi longtemps qu'un motif continue de correspondre. Dans le listing 18-2, nous écrivons une boucle while let qui utilise un vecteur comme pile et imprime les valeurs du vecteur dans l'ordre inverse de celui dans lequel elles ont été empilées.

Nom de fichier : src/main.rs

let mut stack = Vec::new();

stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
    println!("{top}");
}

Listing 18-2: Using a while let loop to print values for as long as stack.pop() returns Some

Cet exemple imprime 3, puis 2, puis 1. La méthode pop retire le dernier élément du vecteur et renvoie Some(value). Si le vecteur est vide, pop renvoie None. La boucle while continue d'exécuter le code dans son bloc aussi longtemps que pop renvoie Some. Lorsque pop renvoie None, la boucle s'arrête. Nous pouvons utiliser while let pour dépiler chaque élément de notre pile.

for Loops

Dans une boucle for, la valeur qui suit directement le mot-clé for est un motif. Par exemple, dans for x in y, le x est le motif. Le listing 18-3 montre comment utiliser un motif dans une boucle for pour déstructurer, c'est-à-dire séparer, un tuple en tant que partie de la boucle for.

Nom de fichier : src/main.rs

let v = vec!['a', 'b', 'c'];

for (index, value) in v.iter().enumerate() {
    println!("{value} is at index {index}");
}

Listing 18-3: Using a pattern in a for loop to destructure a tuple

Le code du listing 18-3 imprimera ce qui suit :

a is at index 0
b is at index 1
c is at index 2

Nous adaptons un itérateur en utilisant la méthode enumerate de sorte qu'il produit une valeur et l'index de cette valeur, placés dans un tuple. La première valeur produite est le tuple (0, 'a'). Lorsque cette valeur est comparée au motif (index, value), index sera 0 et value sera 'a', affichant la première ligne de la sortie.

let Statements

Avant ce chapitre, nous avions seulement explicitement discuté de l'utilisation de motifs avec match et if let, mais en fait, nous les avons utilisés dans d'autres endroits également, y compris dans les instructions let. Par exemple, considérez cette affectation de variable simple avec let :

let x = 5;

Chaque fois que vous avez utilisé une instruction let comme celle-ci, vous avez utilisé des motifs, même si vous ne vous en étiez peut-être pas rendu compte! Plus formellement, une instruction let ressemble à ceci :

let MOTIF = EXPRESSION;

Dans des instructions comme let x = 5; avec un nom de variable dans la case MOTIF, le nom de variable n'est qu'une forme particulièrement simple d'un motif. Rust compare l'expression avec le motif et attribue tous les noms qu'il trouve. Ainsi, dans l'exemple let x = 5;, x est un motif qui signifie "lier ce qui correspond ici à la variable x". Comme le nom x est le motif complet, ce motif signifie effectivement "lier tout à la variable x, quelle que soit la valeur".

Pour voir plus clairement l'aspect de correspondance de motifs de let, considérez le listing 18-4, qui utilise un motif avec let pour déstructurer un tuple.

let (x, y, z) = (1, 2, 3);

Listing 18-4: Using a pattern to destructure a tuple and create three variables at once

Ici, nous comparons un tuple avec un motif. Rust compare la valeur (1, 2, 3) avec le motif (x, y, z) et constate que la valeur correspond au motif, en ce sens qu'il constate que le nombre d'éléments est le même dans les deux, donc Rust lie 1 à x, 2 à y et 3 à z. Vous pouvez considérer ce motif de tuple comme un emboîtement de trois motifs de variable individuels à l'intérieur.

Si le nombre d'éléments dans le motif ne correspond pas au nombre d'éléments dans le tuple, le type global ne correspondra pas et nous obtiendrons une erreur du compilateur. Par exemple, le listing 18-5 montre une tentative de déstructurer un tuple avec trois éléments en deux variables, ce qui ne fonctionnera pas.

let (x, y) = (1, 2, 3);

Listing 18-5: Incorrectly constructing a pattern whose variables don't match the number of elements in the tuple

Tenter de compiler ce code résulte dans cette erreur de type :

error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     let (x, y) = (1, 2, 3);
  |         ^^^^^^   --------- this expression has type `({integer}, {integer},
{integer})`
  |         |
  |         expected a tuple with 3 elements, found one with 2 elements
  |
  = note: expected tuple `({integer}, {integer}, {integer})`
             found tuple `(_, _)`

Pour corriger l'erreur, nous pourrions ignorer une ou plusieurs des valeurs dans le tuple en utilisant _ ou .., comme vous le verrez dans "Ignoring Values in a Pattern". Si le problème est qu'il y a trop de variables dans le motif, la solution est de faire correspondre les types en supprimant des variables de sorte que le nombre de variables soit égal au nombre d'éléments dans le tuple.

Function Parameters

Les paramètres de fonction peuvent également être des motifs. Le code du listing 18-6, qui déclare une fonction nommée foo qui prend un paramètre nommé x de type i32, devrait désormais vous paraître familier.

fn foo(x: i32) {
    // code goes here
}

Listing 18-6: A function signature using patterns in the parameters

La partie x est un motif! Comme nous l'avons fait avec let, nous pouvons comparer un tuple dans les arguments d'une fonction avec le motif. Le listing 18-7 sépare les valeurs d'un tuple lorsqu'on le passe à une fonction.

Nom de fichier : src/main.rs

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({x}, {y})");
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}

Listing 18-7: A function with parameters that destructure a tuple

Ce code imprime Current location: (3, 5). Les valeurs &(3, 5) correspondent au motif &(x, y), donc x est la valeur 3 et y est la valeur 5.

Nous pouvons également utiliser des motifs dans les listes de paramètres de closures de la même manière que dans les listes de paramètres de fonctions car les closures sont similaires aux fonctions, comme discuté au chapitre 13.

À ce stade, vous avez vu plusieurs façons d'utiliser des motifs, mais les motifs ne fonctionnent pas de la même manière dans tous les endroits où nous pouvons les utiliser. Dans certains endroits, les motifs doivent être irréfutables ; dans d'autres circonstances, ils peuvent être réfutable. Nous allons discuter de ces deux concepts ensuite.

Summary

Félicitations! Vous avez terminé le laboratoire Tous les endroits où les motifs peuvent être utilisés. Vous pouvez pratiquer d'autres laboratoires dans LabEx pour améliorer vos compétences.