En Rust, toute la bibliothèque standard de collections est basée sur le concept d'itérateurs. Un itérateur est un objet qui implémente le trait Iterator, dans lequel la méthode next() est définie. Cette méthode renvoie le prochain élément d'une séquence de données (Option<T>, où Some(T) est la valeur suivante, None est la fin de la séquence).
Exemple d'itérateur standard :
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
Adaptateurs d'itérateurs – ce sont des méthodes (par exemple, .map(), .filter(), .enumerate(), .take()) qui retournent de nouveaux itérateurs, traitant les valeurs "à la volée" selon les fonctions passées.
Itérateurs personnalisés sont mis en œuvre en créant une structure et en implémentant le trait Iterator pour elle avec un comportement propre:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
Utilisez des adaptateurs standard lorsque ceux-ci suffisent pour traiter les collections. L'implémentation d'un itérateur personnalisé est nécessaire si :
Peut-on créer des itérateurs infinis ? Que se passe-t-il si l'on essaie de les collecter dans une collection, par exemple avec
.collect()?
Réponse : Oui, en Rust, des itérateurs comme std::iter::repeat existent, retournant une séquence infinie:
let mut endless = std::iter::repeat(1); endless.next(); // retournera Some(1) indéfiniment
Si on tente de collecter un tel itérateur dans une collection via .collect(), le programme se bloquera ou échouera avec un débordement de mémoire, car l'itération ne se terminera pas d'elle-même !
Histoire
Dans un projet REST API, un tableau de 1000 éléments était trié, puis .iter().filter(|x| *x > 500) était utilisé, mais au lieu de .collect::<Vec<_>>(), .fold(0, |acc, _| acc + 1) à l'intérieur d'un adaptateur complexe était appliqué, perdant la compréhension de la manière dont l'itération se terminerait correctement. Résultat : des blocages aléatoires dus au fait que la filtration se produisait par la paresse illimitée de l'itérateur avec un bug interne.
Histoire
Dans un moteur propriétaires pour générer des identifiants uniques, un développeur aléatoire a décidé de mettre en œuvre son itérateur, oubliant de retourner None lors de l'atteinte de la limite. En conséquence, l'itération est devenue une boucle infinie, le serveur en production utilisait tout le CPU et ne répondait pas aux requêtes.
Histoire
Dans la partie frontend (module WebAssembly), un itérateur avec des adaptateurs imbriqués était utilisé : .map().filter().skip(), et ils essayaient d'obtenir le résultat via .collect() pour un formulaire. Lorsqu'ils ont modifié le type sous l'adaptateur, Rust a généré une erreur complexe à la compilation sur une incompatibilité de types, car ils avaient oublié de spécifier le type exact de la collection : .collect::<Vec<_>>(). Le problème a été résolu en ajoutant une annotation, mais ils ont perdu plusieurs heures à chercher la cause.