iter vs into_iter en Rust : ce qui se cache sous le capot ?

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

iter vs into_iter : le choc des titans de l'itération en Rust

\n\n

Si vous avez déjà écrit une boucle sur un vecteur en Rust, vous avez sûrement rencontré les trois frères jumeaux : iter(), into_iter() et iter_mut(). À première vue, ils font tous la même chose : permettre de parcourir les éléments. Mais la différence entre eux est plus profonde qu'il n'y paraît, et elle se trouve au cœur même de Rust : le système de propriété.

\n\n

Imaginez que vous êtes un barista et que votre collection est un ensemble de grains de café. iter() vous permet de regarder les grains, de les sentir, mais pas de les toucher. into_iter(), c'est comme si vous preniez les grains, les mouliez et faisiez un espresso, après quoi les grains disparaissent. Et iter_mut(), vous pouvez non seulement regarder, mais aussi moudre les grains directement dans le sac, en changeant leur forme.

\n\n

Aujourd'hui, nous allons décortiquer iter vs into_iter comme on l'explique sur Stack Overflow, mais avec des exemples concrets et une plongée en profondeur sous le capot. C'est parti !

\n\n

Qu'est-ce qu'un itérateur en Rust ?

\n\n

Un itérateur est un objet qui sait comment obtenir séquentiellement des éléments d'une collection. En Rust, les itérateurs sont paresseux : ils ne font rien tant que vous n'appelez pas next(). Mais le principal est que chaque méthode d'itération traite la propriété des données différemment.

\n\n

Voici les trois principales approches :

\n
    \n
  • iter() — emprunte les éléments (ne prend pas la propriété)
  • \n
  • into_iter() — consomme la collection (prend la propriété)
  • \n
  • iter_mut() — emprunte avec possibilité de modification
  • \n
\n\n

Regardons le code. Supposons que nous ayons un vecteur de prénoms :

\n\n
let names = vec!["Anna".to_string(), "Boris".to_string(), "Victor".to_string()];
\n\n

Exemple 1 : iter() — juste regarder

\n\n
fn main() {\n    let names = vec!["Anna".to_string(), "Boris".to_string(), "Victor".to_string()];\n    \n    // iter() retourne &String (une référence)\n    for name in names.iter() {\n        println!("Bonjour, {} !", name);\n    }\n    \n    // names est toujours accessible !\n    println!("Le vecteur est vivant : {:?}", names);\n}
\n\n

Remarquez qu'après la boucle, nous utilisons names sans problème. Pourquoi ? Parce que iter() a retourné une référence à chaque élément. La propriété reste au vecteur. C'est sûr et prévisible.

\n\n

Exemple 2 : into_iter() — brûler les ponts

\n\n
fn main() {\n    let names = vec!["Anna".to_string(), "Boris".to_string(), "Victor".to_string()];\n    \n    // into_iter() retourne String (la propriété est déplacée)\n    for name in names.into_iter() {\n        println!("Adieu, {} !", name);\n    }\n    \n    // Erreur ! names n'existe plus\n    // println!("Le vecteur est vivant : {:?}", names); // ❌ emprunt d'une valeur déplacée\n}
\n\n

Voilà la différence clé. into_iter() consomme la collection. Chaque élément est déplacé dans la boucle, et le vecteur d'origine devient invalide. Si vous devez conserver la collection pour une utilisation ultérieure, into_iter() n'est pas votre choix.

\n\n

Exemple 3 : iter_mut() — modifier sans détruire

\n\n
fn main() {\n    let mut names = vec!["Anna".to_string(), "Boris".to_string(), "Victor".to_string()];\n    \n    // iter_mut() retourne &mut String (une référence mutable)\n    for name in names.iter_mut() {\n        name.push_str(" (invité)");\n    }\n    \n    println!("Vecteur modifié : {:?}", names);\n    // Sortie : ["Anna (invité)", "Boris (invité)", "Victor (invité)"]\n}
\n\n

Ici, nous modifions les éléments sur place, sans prendre la propriété. Idéal pour les transformations.

\n\n

Sous le capot : que retourne chaque méthode ?

\n\n

Pour vraiment comprendre iter vs into_iter, regardons dans la bibliothèque standard. Pour un vecteur Vec<T> :

\n\n
    \n
  • iter() retourne std::slice::Iter<T> — implémente Iterator<Item = &T>

Blogs

Book Recommendations