Ich denke, es gibt noch etwas zu klären. Auflistungstypen wie Vec<T>
und VecDeque<T>
haben eine into_iter
Methode, die sich ergibt, T
weil sie implementiert werden IntoIterator<Item=T>
. Nichts hindert uns daran, einen Typ zu erstellen, der, Foo<T>
wenn er wiederholt wird, T
nur 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_iter
unditer
Zurück zur ursprünglichen Frage zum Unterschied zwischen into_iter
und iter
. Ähnlich wie andere darauf hingewiesen haben, besteht der Unterschied darin, dass into_iter
es sich um eine erforderliche Methode handelt, IntoIterator
die 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: iter
und iter_mut
deren Ausbeute &I
und &mut I
, respectively.
Dies impliziert, dass wir eine Funktion erstellen können, die einen Typ mit einer into_iter
Methode 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 iter
Verfahren oder iter_mut
Verfahren, weil sie nur Konventionen sind. Wir können sagen, dass dies into_iter
weiter verbreitet ist als iter
oder iter_mut
.
Alternativen zu iter
unditer_mut
Ein weiteres interessantes Merkmal ist, dass dies iter
nicht der einzige Weg ist, einen Iterator zu erhalten, der nachgibt &T
. Gemäß der Konvention (wieder) werden bei Sammlungstypen, SomeCollection<T>
bei std
denen eine iter
Methode 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 &v
in beiden Implementierungen gleichwertig ist IntoIterator<Item=&T>
, warum stellt Rust dann beide bereit? Es ist für die Ergonomie. In for
Schleifen ist die Verwendung etwas prägnanter &v
als 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 for
Schleifen 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_iter
und iter
Methoden 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 String
bietet weder into_iter
noch, iter
weil 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 bytes
bereitgestellt : zum Iterieren der Bytes und chars
zum Iterieren der Zeichen als Alternative zur iter
Methode.
* Nun, technisch können wir es tun, indem wir ein Merkmal erstellen. Aber dann brauchen wir impl
dieses Merkmal für jeden Typ, den wir verwenden möchten. Mittlerweile sind viele Typen std
bereits implementiert IntoIterator
.