Ich denke, es gibt noch etwas zu klären. Auflistungstypen wie Vec<T>und VecDeque<T>haben eine into_iterMethode, die sich ergibt, Tweil sie implementiert werden IntoIterator<Item=T>. Nichts hindert uns daran, einen Typ zu erstellen, der, Foo<T>wenn er wiederholt wird, Tnur einen anderen Typ ergibt U. Das heißt, Foo<T>implementiert IntoIterator<Item=U>.
In der Tat gibt es einige Beispiele in std: &Path Geräte IntoIterator<Item=&OsStr> und &UnixListener Geräte IntoIterator<Item=Result<UnixStream>> .
Der Unterschied zwischen into_iterunditer
Zurück zur ursprünglichen Frage zum Unterschied zwischen into_iterund iter. Ähnlich wie andere darauf hingewiesen haben, besteht der Unterschied darin, dass into_iteres sich um eine erforderliche Methode handelt, IntoIteratordie jeden in angegebenen Typ ergeben kann IntoIterator::Item. Typischerweise wird , wenn ein Typ implementiert IntoIterator<Item=I>, durch Konvention es auch zwei Ad-hoc - Methoden hat: iterund iter_mutderen Ausbeute &Iund &mut I, respectively.
Dies impliziert, dass wir eine Funktion erstellen können, die einen Typ mit einer into_iterMethode empfängt (dh eine iterierbare), indem wir ein gebundenes Merkmal verwenden:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Wir können jedoch nicht * ein Merkmal verwenden verpflichtet , eine Art zu verlangen, haben iterVerfahren oder iter_mutVerfahren, weil sie nur Konventionen sind. Wir können sagen, dass dies into_iterweiter verbreitet ist als iteroder iter_mut.
Alternativen zu iterunditer_mut
Ein weiteres interessantes Merkmal ist, dass dies iternicht der einzige Weg ist, einen Iterator zu erhalten, der nachgibt &T. Gemäß der Konvention (wieder) werden bei Sammlungstypen, SomeCollection<T>bei stddenen eine iterMethode vorhanden ist, auch unveränderliche Referenztypen &SomeCollection<T>implementiert IntoIterator<Item=&T>. Zum Beispiel &Vec<T> implementiert IntoIterator<Item=&T> , so dass wir wiederholen können &Vec<T>:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
Wenn v.iter()dies &vin beiden Implementierungen gleichwertig ist IntoIterator<Item=&T>, warum stellt Rust dann beide bereit? Es ist für die Ergonomie. In forSchleifen ist die Verwendung etwas prägnanter &vals v.iter(); aber in anderen Fällen v.iter()ist viel klarer als (&v).into_iter():
let v = vec![1, 2];
let a: Vec<i32> = v.iter().map(|x| x * x).collect();
// Although above and below are equivalent, above is a lot clearer than below.
let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();
Ebenso kann in forSchleifen v.iter_mut()ersetzt werden durch &mut v:
let mut v = vec![1, 2];
// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
*item *= 2;
}
Wann (implementiert) into_iterund iterMethoden für einen Typ
Wenn der Typ nur einen "Weg" hat, über den iteriert werden kann, sollten wir beide implementieren. Wenn es jedoch zwei oder mehr Möglichkeiten gibt, die wiederholt werden können, sollten wir stattdessen für jede Möglichkeit eine Ad-hoc-Methode bereitstellen.
Zum Beispiel Stringbietet weder into_iternoch, iterweil es zwei Möglichkeiten gibt, es zu iterieren: seine Darstellung in Bytes zu iterieren oder seine Darstellung in Zeichen zu iterieren. Stattdessen werden zwei Methoden bytesbereitgestellt : zum Iterieren der Bytes und charszum Iterieren der Zeichen als Alternative zur iterMethode.
* Nun, technisch können wir es tun, indem wir ein Merkmal erstellen. Aber dann brauchen wir impldieses Merkmal für jeden Typ, den wir verwenden möchten. Mittlerweile sind viele Typen stdbereits implementiert IntoIterator.