Thanks!
X
Paypal
Github sponsorship
Patreon
Acheter version papier

Rust

Fork me on GitHub

I. Les bases de la programmation en Rust

5. Conditions et pattern matching

Nous allons d'abord commencer par les conditions :

if / else if / else

Les if / else if / else fonctionnent de la même façon qu'en C/C++/Java :

Runlet age: i32 = 17;

if age >= 18 {
    println!("majeur !");
} else {
    println!("mineur !");
}

Vous aurez noté que je n'ai pas mis de parenthèses, ( et ), autour des conditions : elles sont superflues en Rust. Cependant, elles seront nécessaires si vous faites des "sous"-conditions :

Runif age > 18 && (age == 20 || age == 24) {
    println!("ok");
}

Par contre, les accolades { et } sont obligatoires, même si le bloc de votre condition ne contient qu'une seule ligne de code !

Pour rappel : && est la condition et tandis que || est la condition ou. Donc dans le code au-dessus, il faut le lire comme ceci :

age doit être supérieur à 18 ET age doit être égal à 20 OU 24.

Comparaison de booléens

Si vous souhaitez faire une comparaison avec un booléen (donc true ou false), sachez qu'il est possible de les écrire de façon plus courte :

Runif x == true {}
// est équivalent à:
if x {}

if x == false {}
// est équivalent à:
if !x {}

Pattern matching

Rappelons dans un premier temps la définition du pattern matching ou "filtrage par motif" : c'est la vérification de la présence de constituants d'un motif par un programme informatique ou parfois par un matériel spécialisé.

Pour dire les choses plus simplement, c'est une condition permettant de faire les choses de manière différente. Grâce à lui, on peut comparer ce que l'on appelle des expressions de manière plus intuitive (nous aborderons les expressions un peu plus loin dans ce cours). Si vous avez déjà utilisé des langages fonctionnels, vous ne vous sentirez pas dépaysés.

Regardons un exemple pour faciliter les explications :

Runlet my_string = "hello";

match my_string {
    "bonjour" => {
        println!("français");
    }
    "ciao" => {
        println!("italien");
    }
    "hello" => {
        println!("anglais");
    }
    "hola" => {
        println!("espagnol");
    }
    _ => {
        println!("je ne connais pas cette langue...");
    }
}

Ici ça affichera "anglais".

Comme vous vous en doutez, on peut s'en servir sur n'importe quel type de variable. Après tout, il sert à comparer des expressions, vous pouvez très bien matcher sur un i32 ou un f64 si vous en avez besoin.

Concernant le _ utilisé dans la dernière branche du match, il s'agit d'une variable (nommée ainsi pour éviter un warning pour variable non utilisée, _a aurait eu le même résultat) qui contient "toutes les autres expressions". C'est en quelque sorte le else du pattern matching (il fonctionne de la même manière que le default d'un switch C/C++/Java). Il est obligatoire de l'utiliser si toutes les expressions possibles n'ont pas été testées ! Dans le cas présent, il est impossible de tester toutes les strings existantes, on utilise donc _ à la fin. Si on teste un booléen, il n'y aura que deux valeurs possibles et il sera possible de les tester toutes les deux :

Runlet b = true;

match b {
    true => {
        // faire quelque chose
    }
    false => {
        // faire autre chose
    }
}

Un autre exemple en utilisant un i32 :

Runlet age: i32 = 18;

match age {
    17 => {
        println!("mineur !");
    }
    18 => {
        println!("majeur !");
    }
    x => {
        println!("ni 17, ni 18 mais {} !", x);
    }
}

Il est bien évidemment possible de matcher plus d'une valeur à la fois, pas besoin d'écrire toutes les valeurs en dessous de 18 pour voir s'il est mineur. Si on reprend le précédent exemple, le mieux serait d'écrire :

Runlet age: i32 = 17;

match age {
    tmp if tmp > 60 => {
        println!("plus tout jeune...");
    }
    tmp if tmp > 17 => {
        println!("majeur !");
    }
    _ => {
        println!("mineur !");
    }
}

Comme vous l'avez sans doute compris, il est possible d'ajouter une condition à un pattern matching. Elle nous permet d'affiner les résultats ! On peut donc ajouter des && ou des || selon les besoins.

Toujours plus loin !

Il est aussi possible de matcher directement sur un ensemble de valeurs de cette façon :

Runlet i = 0i32;

match i {
    10..=100 => println!("La variable est entre 10 et 100 (inclus)"),
    x => println!("{} n'est pas entre 10 et 100 (inclus)", x)
};

À noter, dans le cas d’un for i in 10..100 { println!("{}", i); }, i prendra une valeur allant de 10 à 99 inclus.

Pratique ! Vous pouvez aussi "binder" (ou matcher sur un ensemble de valeurs) la variable avec le symbole "@" :

Runlet i = 0i32;

match i {
    x @ 10..=100 => println!("{} est entre 10 et 100 (inclus)", x),
    x => println!("{} n'est pas entre 10 et 100 (inclus)", x)
};

Il ne nous reste maintenant plus qu'un dernier point à aborder :

Runmatch une_variable {
    "jambon" | "poisson" | "œuf" => println!("Des protéines !"),
    "bonbon" => println!("Des bonbons !"),
    "salade" | "épinards" | "fenouil" => println!("Beurk ! Des légumes !"),
    _ => println!("ça, je sais pas ce que c'est..."),
}

Vous l'aurez sans doute deviné : ici, le '|' sert de condition "ou" sur le pattern. Dans le premier cas, si une_variable vaut "jambon", "poisson" ou "œuf", le match rentrera dans cette condition, et ainsi de suite.

Voilà qui clôt ce chapitre sur les conditions et le pattern matching. N'hésitez pas à revenir sur les points que vous n'êtes pas sûr d'avoir parfaitement compris, car il s'agit vraiment de la base de ce langage. Si quelque chose n'est pas parfaitement maîtrisé, vous risquez d'avoir du mal à comprendre la suite.