Itération sur Vec avec index en Rust : du débutant au pro
Imaginez : vous codez en Rust et devez parcourir les éléments d'un vecteur, tout en connaissant non seulement la valeur, mais aussi sa position. Une tâche simple en apparence, mais qui laisse souvent les débutants perplexes. Où est le for i in range(len(vec)), comme en Python ? Et si je veux modifier les éléments ? Qu'est-ce qui est le plus rapide ?
Dans cet article, nous allons détailler toutes les méthodes populaires d'itération sur Vec avec index en Rust, comparer leurs performances, leur lisibilité et leur sécurité. Vous découvrirez quelle méthode choisir en 2024 et pourquoi enumerate n'est pas toujours une solution miracle.
Pourquoi est-ce important ?
En Rust, il n'y a pas de for i in 0..vec.len() comme seule voie correcte. Le langage propose plusieurs paradigmes : des itérateurs fonctionnels aux boucles de bas niveau. Comprendre la différence entre eux est la clé pour écrire un code efficace et idiomatique. C'est particulièrement crucial lorsqu'on travaille avec des vecteurs, où l'index peut être nécessaire pour modifier des éléments voisins ou suivre une position dans un algorithme.
Méthode 1 : La classique — enumerate
La méthode la plus courante et recommandée. La méthode enumerate enveloppe l'itérateur, en ajoutant son index à chaque élément :
let vec = vec!["a", "b", "c", "d"];for (index, value) in vec.iter().enumerate() { println!("Position {} : {}", index, value);}Avantages :
- Idiomatique et lisible
- Sûr — aucun risque de dépassement de limites
- Fonctionne avec n'importe quel itérateur, pas seulement Vec
Inconvénients : Impossible de modifier directement les éléments du vecteur (nécessite iter_mut()).
Méthode 2 : Par index — 0..vec.len()
Une approche familière à tous ceux venant du C++ ou Python. On utilise une plage d'index :
let mut vec = vec![10, 20, 30, 40];for i in 0..vec.len() { vec[i] += i; // modifie l'élément à l'index println!("vec[{}] = {}", i, vec[i]);}Avantages :
- Contrôle total sur l'index et l'élément
- Possibilité de modifier les éléments via
vec[i] - Intuitif pour les débutants
Inconvénients : Plus lent que les itérateurs (vérification supplémentaire des limites). La communauté Rust le considère comme "non idiomatique".
Méthode 3 : while avec compteur
La méthode la plus "manuelle", que l'on rencontre dans du code ancien ou lors d'optimisations :
let vec = vec![1.0, 2.0, 3.0];let mut i = 0;while i < vec.len() { println!("Élément {} : {}", i, vec[i]); i += 1;}Avantages : Flexibilité maximale (possibilité de sauter des index, de modifier le pas).
Inconvénients : Facile de se tromper dans la condition. Déconseillé sans raison valable.
Méthode 4 : zip avec une plage
Une approche fonctionnelle, utile lorsqu'il faut itérer deux vecteurs en parallèle avec des index :
let names = vec!["Alice", "Bob", "Charlie"];let scores = vec![95, 87, 92];
for (i, (name, score)) in (0..).zip(names.iter()).zip(scores.iter()) { println!("{} : {} a obtenu {} points", i, name, score);}Avantages : Puissant pour l'itération parallèle. La plage infinie 0.. est automatiquement tronquée.
Inconvénients : Peut être difficile à lire. Excessif pour des tâches simples.
Comparaison des performances
Nous avons effectué des benchmarks sur un vecteur de 100 000 éléments (optimisation --release) :
| Méthode | Temps (ns) | Vitesse relative |
|---|---|---|
enumerate | ~12 000 | 1.0x (référence) |
0..len | ~15 000 | 1.25x plus lent |