Aller au contenu

La boucle while en Rust

Une boucle while continue de tourner tant qu’une condition reste true. C’est le bon outil quand vous savez ce que vous attendez, même si vous ne savez pas exactement combien de tours il faudra.

fn main() {
let mut count = 1;
while count <= 5 {
println!("{count}");
count += 1;
}
}

Sortie :

1
2
3
4
5
▶ Essayez par vous-même

La condition (count <= 5) est vérifiée avant chaque tour de boucle. Dès qu’elle vaut false, la boucle s’arrête : ici, quand count passe à 6.

Vous pourriez écrire la même chose avec loop et un if/break à la main, mais while exprime votre intention plus directement : « continue tant que c’est vrai ». Gardez loop pour les cas où la condition d’arrêt ne trouve pas naturellement sa place en haut de la boucle.

// avec while : clair et direct
let mut n = 0;
while n < 3 {
println!("{n}");
n += 1;
}
// la même chose avec loop : ça marche, mais c'est plus long à lire
let mut n = 0;
loop {
if n >= 3 {
break;
}
println!("{n}");
n += 1;
}

On utilise souvent while pour attendre qu’une valeur atteigne un objectif :

fn main() {
let mut balance = 100;
while balance < 200 {
balance += 25;
println!("Le solde est maintenant de {balance}");
}
println!("Objectif atteint !");
}

Sortie :

Le solde est maintenant de 125
Le solde est maintenant de 150
Le solde est maintenant de 175
Le solde est maintenant de 200
Objectif atteint !
▶ Essayez par vous-même

La condition peut être n’importe quelle expression qui donne un bool. Vous pouvez donc combiner plusieurs tests avec && et ||, vus à la page Opérateurs, par exemple while balance < 200 && months < 12.

Certains langages ont une boucle do { ... } while (condition). Elle exécute le corps d’abord et vérifie la condition ensuite, donc le corps s’exécute toujours au moins une fois. Rust ne l’a pas. À la place, on utilise loop et on place le test à la fin.

Voici un cas où ça compte. Pour compter les chiffres d’un nombre, on peut le diviser par 10 jusqu’à ce qu’il ne reste plus rien. Mais le nombre 0 a quand même un chiffre, et une simple boucle while ne s’exécute jamais pour lui :

fn main() {
let number = 0;
// Une boucle while vérifie d'abord, donc elle ne tourne jamais pour 0.
let mut n = number;
let mut digits = 0;
while n > 0 {
digits += 1;
n /= 10;
}
println!("selon while, {number} a {digits} chiffre(s)");
// loop + un test à la fin exécute le corps au moins une fois.
let mut n = number;
let mut digits = 0;
loop {
digits += 1;
n /= 10;
if n == 0 {
break;
}
}
println!("selon loop, {number} a {digits} chiffre(s)");
}

Sortie :

selon while, 0 a 0 chiffre(s)
selon loop, 0 a 1 chiffre(s)
▶ Essayez par vous-même

Remplacez number par 4096 et les deux versions sont d’accord : 4 chiffres. La différence n’apparaît que lorsque la condition est fausse dès le départ. D’où la règle pratique : si le corps doit s’exécuter au moins une fois, écrivez loop avec le if ... { break; } en bas.

Il existe encore un autre type de boucle while, while let, qui continue tant qu’une valeur correspond à un pattern (motif). Elle est expliquée à la page If let et while let.

Additionnez 1 + 2 + 3 + … et arrêtez-vous dès que le total est strictement supérieur à 100. Affichez combien de nombres vous avez additionnés et le total. Indice : il vous faut deux variables mut, une pour le prochain nombre et une pour le total.

Voir la solution
fn main() {
let mut next = 1;
let mut total = 0;
while total <= 100 {
total += next;
next += 1;
}
println!("{} nombres additionnés, le total est {total}", next - 1);
}

Sortie :

14 nombres additionnés, le total est 105
▶ Essayez par vous-même

Après le dernier tour, next a déjà avancé d’un cran, c’est pourquoi on affiche next - 1. Ce genre de décalage d’une unité mérite d’être vérifié de tête sur un petit exemple.

Partez de 1000. Tant que le nombre est supérieur à 1, divisez-le par 2 (division entière) et affichez-le. À la fin, affichez combien de divisions il a fallu. Indice : n /= 2 divise n par 2 et laisse tomber le reste.

Voir la solution
fn main() {
let mut n = 1000;
let mut steps = 0;
while n > 1 {
n /= 2;
steps += 1;
println!("{n}");
}
println!("Il a fallu {steps} divisions par 2 pour arriver à 1");
}

Sortie :

500
250
125
62
31
15
7
3
1
Il a fallu 9 divisions par 2 pour arriver à 1
▶ Essayez par vous-même

125 / 2 donne 62, pas 62.5, parce que les deux nombres sont des entiers. C’est pour ça que la boucle s’arrête pile à 1.

  • while condition { ... } répète le corps tant que la condition est true, en la vérifiant avant chaque tour.
  • Assurez-vous que quelque chose dans la boucle finit par rendre la condition false, sinon elle tournera indéfiniment.
  • Préférez while à loop dès que la condition d’arrêt s’exprime naturellement par « tant que X est vrai ».
  • Rust n’a pas de do-while. Quand le corps doit s’exécuter au moins une fois, utilisez loop avec le test du break à la fin.

Petit quiz

1. Quelle est l'erreur classique qui provoque une boucle while infinie ?

2. D'après cette page, quand faut-il préférer while à loop ?

3. Comment écrire une boucle dont le corps s'exécute toujours au moins une fois ?

Score : 0 / 3