iter vs into_iter: Kampf der Titanen der Iteration in Rust
\n\nWenn Sie jemals in Rust eine Schleife über einen Vektor geschrieben haben, sind Sie bestimmt auf die drei Zwillingsbrüder gestoßen: iter(), into_iter() und iter_mut(). Auf den ersten Blick tun sie alle dasselbe – sie ermöglichen es, über die Elemente zu iterieren. Aber der Unterschied zwischen ihnen ist tiefer, als es scheint, und liegt im Herzen von Rust – dem Ownership-System.
Stellen Sie sich vor, Sie sind ein Barista und Ihre Sammlung sind Kaffeebohnen. iter() erlaubt Ihnen, die Bohnen anzusehen, an ihnen zu riechen, aber sie nicht zu berühren. into_iter() ist, wenn Sie die Bohnen nehmen, mahlen und einen Espresso machen, wonach die Bohnen verschwinden. Und iter_mut() – Sie können nicht nur schauen, sondern die Bohnen direkt im Beutel mahlen und ihre Form verändern.
Heute werden wir iter vs into_iter so aufschlüsseln, wie es auf Stack Overflow erklärt wird, aber mit lebendigen Beispielen und einem tiefen Einblick unter die Haube. Los geht's!
\n\nWas ist ein Iterator in Rust?
\n\nEin Iterator ist ein Objekt, das weiß, wie man sequenziell Elemente aus einer Sammlung erhält. In Rust sind Iteratoren faul: Sie tun nichts, bis Sie next() aufrufen. Aber das Wichtigste ist, dass jede Iterationsmethode das Ownership der Daten unterschiedlich behandelt.
Hier sind die drei Hauptansätze:
\n- \n
iter()– leiht sich die Elemente aus (übernimmt kein Ownership) \n into_iter()– konsumiert die Sammlung (übernimmt Ownership) \n iter_mut()– leiht mit der Möglichkeit zur Änderung aus \n
Schauen wir uns den Code an. Angenommen, wir haben einen Vektor mit Namen:
\n\nlet names = vec!["Anna".to_string(), "Boris".to_string(), "Viktor".to_string()];\n\nBeispiel 1: iter() – nur anschauen
\n\nfn main() {\n let names = vec!["Anna".to_string(), "Boris".to_string(), "Viktor".to_string()];\n \n // iter() gibt &String zurück (eine Referenz)\n for name in names.iter() {\n println!("Hallo, {}!", name);\n }\n \n // names ist immer noch verfügbar!\n println!("Vektor lebt: {:?}", names);\n}\n\nBeachten Sie: Nach der Schleife verwenden wir names problemlos weiter. Warum? Weil iter() eine Referenz auf jedes Element zurückgegeben hat. Das Ownership blieb beim Vektor. Das ist sicher und vorhersehbar.
Beispiel 2: into_iter() – die Brücken abbrechen
\n\nfn main() {\n let names = vec!["Anna".to_string(), "Boris".to_string(), "Viktor".to_string()];\n \n // into_iter() gibt String zurück (Ownership wird verschoben)\n for name in names.into_iter() {\n println!("Tschüss, {}!", name);\n }\n \n // Fehler! names existiert nicht mehr\n // println!("Vektor lebt: {:?}", names); // ❌ borrow of moved value\n}\n\nDa haben wir es, der entscheidende Unterschied. into_iter() konsumiert die Sammlung. Jedes Element wird in die Schleife verschoben, und der ursprüngliche Vektor wird ungültig. Wenn Sie die Sammlung für die weitere Verwendung behalten müssen – ist into_iter() nicht Ihre Wahl.
Beispiel 3: iter_mut() – ändern, ohne zu zerstören
\n\nfn main() {\n let mut names = vec!["Anna".to_string(), "Boris".to_string(), "Viktor".to_string()];\n \n // iter_mut() gibt &mut String zurück (eine mutable Referenz)\n for name in names.iter_mut() {\n name.push_str(" (Gast)");\n }\n \n println!("Geänderter Vektor: {:?}", names);\n // Ausgabe: ["Anna (Gast)", "Boris (Gast)", "Viktor (Gast)"]\n}\n\nHier ändern wir die Elemente direkt, ohne das Ownership zu übernehmen. Ideal für Transformationen.
\n\nUnter der Haube: Was gibt jede Methode zurück?
\n\nUm iter vs into_iter wirklich zu verstehen, werfen wir einen Blick in die Standardbibliothek. Für den Vektor Vec<T>:
- \n
iter()gibtstd::slice::Iter<T>zurück – implementiertIterator<Item = &T>