I. Les bases de la programmation en Rust
8. Les boucles
Les boucles sont l'une des bases de la programmation, il est donc impératif de regarder comment elles fonctionnent en Rust.
while
Comme dans les autres langages, la boucle while continue tant que sa condition est respectée. Exemple :
Runlet mut i: i32 = 0;
while i < 10 {
println!("bonjour !");
i += 1;
}
Ici, le programme affichera bonjour tant que i
sera inférieur à 10.
Il faut cependant faire attention à ces deux éléments :
- Notez bien qu'il n'y a pas de parenthèse autour de la condition (
i < 10
). - Les accolades sont obligatoires !
loop
Il existe aussi la possibilité d'écrire des boucles infinies avec le mot clé loop (plutôt qu'un while true
) :
Il est assez courant d'écrire des boucles infinies mais prenons un cas pratique de leur utilisation : un jeu vidéo. L'affichage doit alors continuer en permanence jusqu'à ce que l'on quitte. Donc plutôt que d'écrire :
Runwhile true {
//...
}
// ou
let mut end = false;
while !end {
//...
}
On écrira :
Runloop {
//...
}
Pour sortir d'une boucle infinie, il y a deux solutions :
- Utiliser le mot-clé break.
- Utiliser le mot-clé return.
Reprenons notre exemple du début et modifions-le un peu pour utiliser loop à la place :
Runlet mut i: i32 = 0;
loop {
println!("bonjour !");
i += 1;
if i > 10 {
break; // On arrête la boucle.
}
}
Petit rappel concernant les mots-clés break et return : le mot-clé break permet seulement de quitter la boucle courante :
Runloop { // Première boucle
println!("Toujours là !");
let mut i = 0i32;
loop { // Deuxième boucle.
println!("sous-boucle !");
i += 1;
if i > 2 {
// On reprend l'exécution de "Première boucle".
break;
}
}
}
Tandis que le mot-clé return fait quitter la fonction courante :
Runfn boucle_et_print() {
loop {
println!("Toujours là !");
let mut i = 0i32;
loop {
println!("sous-boucle !");
i += 1;
if i > 2 {
// On quitte la fonction "boucle_et_print".
return;
}
}
}
}
for
La boucle for est un peu plus complexe que les deux précédentes. Elle ne fonctionne qu'avec des objets implémentant le trait IntoIterator. À ce stade nous n'avons pas encore vu ce qu'est un trait, mais nous y reviendrons plus tard. Toutefois, la compréhension exacte du fonctionnement des traits n'est pas indispensable pour comprendre le fonctionnement de for. Regardons dès à présent quelques exemples :
Runfor i in 0..10 {
println!("i vaut : {}", i);
}
Ce qui va afficher :
i vaut : 0
i vaut : 1
i vaut : 2
i vaut : 3
i vaut : 4
i vaut : 5
i vaut : 6
i vaut : 7
i vaut : 8
i vaut : 9
La variable i, créée pour la boucle for, prendra successivement toutes les valeurs allant de 0 à 9, puis la boucle prendra fin.
Maintenant revenons sur ce 0..10
: c'est un objet de type Range qui implémente le trait IntoIterator, nous permettant d'itérer dessus.
Prenons un deuxième exemple avec un Vec cette fois :
Runlet v = vec![1, 4, 5, 10, 6]; // On crée un vecteur qui contient ces valeurs.
for value in v { // Puis on itère sur les valeurs de ce vecteur.
println!("{}", value);
}
Ce qui va afficher :
1
4
5
10
6
Donc comme indiqué, si votre type implémente le trait IntoIterator, vous pouvez utiliser la boucle for pour itérer dessus.
Énumération
Si vous souhaitez savoir combien de fois vous avez itéré, vous pouvez utiliser la fonction enumerate :
Runfor (position, valeur) in (6..10).enumerate() {
println!("position = {} et valeur = {}", position, valeur);
}
Ce qui affichera :
position = 0 et valeur = 6
position = 1 et valeur = 7
position = 2 et valeur = 8
position = 3 et valeur = 9
position vaut donc le nombre d'itérations effectuées à l'intérieur de la boucle tandis que valeur prend successivement les valeurs du range 6..10
. Autre exemple :
Runlet v = vec!["a", "b", "c", "d"]; // On crée un vecteur.
for (position, value) in v.iter().enumerate() { // On itère sur ses valeurs.
println!("position = {} et value = \"{}\"", position, value);
}
Ce qui affichera :
position = 0 et value = "a"
position = 1 et value = "b"
position = 2 et value = "c"
position = 3 et value = "d"
Les boucles nommées
Encore une autre chose intéressante à connaître : les boucles nommées ! Mieux vaut commencer par un exemple :
Run// 'outer désigne le nom ou label de la boucle ci-dessous :
'outer: for x in 0..10 {
'inner: for y in 0..10 {
// on continue la boucle sur x
if x % 2 == 0 { continue 'outer; }
// on continue la boucle sur y
if y % 2 == 0 { continue 'inner; }
println!("x: {}, y: {}", x, y);
}
}
Je pense que vous l'aurez compris, on peut directement reprendre ou arrêter une boucle en utilisant son nom (pour peu que vous lui en ayez donné un bien évidemment). Autre exemple :
Run'global: for _ in 0..10 {
'outer: for x in 0..10 {
'inner: for y in 0..10 {
// on arrête la boucle qui s'appelle global
if x > 3 { break 'global; }
// on continue la boucle sur x
if x % 2 == 0 { continue 'outer; }
// on continue la boucle sur y
if y % 2 == 0 { continue 'inner; }
println!("x: {}, y: {}", x, y);
}
}
}
Encore une fois, je vous invite à tester pour bien comprendre comment tout ça fonctionne.