TL; DR:
- Der Iterator zurück durch
into_iter
kann jede Ausbeute T
, &T
oder &mut T
, abhängig von dem Kontext.
- Der von zurückgegebene Iterator
iter
ergibt gemäß &T
Konvention.
- Der von zurückgegebene Iterator
iter_mut
ergibt gemäß &mut T
Konvention.
Die erste Frage lautet: "Was ist into_iter
?"
into_iter
kommt von der IntoIterator
Eigenschaft :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Sie implementieren dieses Merkmal, wenn Sie angeben möchten, wie ein bestimmter Typ in einen Iterator konvertiert werden soll. Insbesondere wenn ein Typ implementiert wird IntoIterator
, kann er in einer for
Schleife verwendet werden.
Zum Beispiel Vec
implementiert IntoIterator
... dreimal!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Jede Variante ist etwas anders.
Dieser verbraucht den Vec
und sein Iterator liefert Werte ( T
direkt):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Die anderen beiden nehmen den Vektor als Referenz (lassen Sie sich nicht von der Signatur täuschen, into_iter(self)
da self
es sich in beiden Fällen um eine Referenz handelt), und ihre Iteratoren erzeugen Verweise auf die darin enthaltenen Elemente Vec
.
Dieser liefert unveränderliche Referenzen :
impl<'a, T> IntoIterator for &'a Vec<T> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}
Während dieser veränderliche Referenzen liefert :
impl<'a, T> IntoIterator for &'a mut Vec<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}
So:
Was ist der Unterschied zwischen iter
und into_iter
?
into_iter
ist eine generische Methode, um einen Iterator zu erhalten. Ob dieser Iterator Werte, unveränderliche Referenzen oder veränderbare Referenzen liefert, ist kontextabhängig und kann manchmal überraschend sein.
iter
und iter_mut
sind Ad-hoc-Methoden. Ihr Rückgabetyp ist daher unabhängig vom Kontext und wird üblicherweise Iteratoren sein, die unveränderliche Referenzen bzw. veränderbare Referenzen liefern.
Der Autor des Beitrags "Rust by Example" veranschaulicht die Überraschung, die sich aus der Abhängigkeit vom Kontext (dh dem Typ) into_iter
ergibt, von dem aufgerufen wird, und verschärft das Problem auch, indem er Folgendes verwendet:
IntoIterator
ist nicht implementiert für [T; N]
, nur für &[T; N]
und&mut [T; N]
- Wenn eine Methode für einen Wert nicht implementiert ist , wird stattdessen automatisch nach Verweisen auf diesen Wert gesucht
Dies ist sehr überraschend, into_iter
da alle Typen (außer [T; N]
) es für alle 3 Variationen (Wert und Referenzen) implementieren. Es ist dem Array nicht möglich, einen Iterator zu implementieren, der Werte liefert, da es nicht "schrumpfen" kann, um seine Elemente aufzugeben.
Warum Arrays IntoIterator
(auf so überraschende Weise) implementiert werden : Sie sollen es ermöglichen, Verweise auf sie in for
Schleifen zu durchlaufen .
into_iter
eine Implementierung basierend darauf ausgewählt wird, ob der Empfänger ein Wert, eine Referenz oder eine veränderbare Referenz ist. (2) Es gibt keine veränderlichen Werte in Rust, oder vielmehr ist jeder Wert veränderbar, da Sie Eigentümer sind.&'a MyStruct
und&mut 'a MyStruct
und die erste wurde immer gewählt , falls vorhanden , auch wenn ich riefinto_iter().for_each()
anmut
Wert mit&mut
Argumenten in Lambda.Ich (ein Rust-Neuling) kam von Google hierher und suchte nach einer einfachen Antwort, die von den anderen Antworten nicht bereitgestellt wurde. Hier ist diese einfache Antwort:
iter()
iteriert über die Elemente als Referenzinto_iter()
iteriert über die Elemente und verschiebt sie in den neuen Bereichiter_mut()
iteriert über die Elemente und gibt jedem Element einen veränderlichen VerweisAlso
for x in my_vec { ... }
ist im Wesentlichen gleichbedeutend mitmy_vec.into_iter().for_each(|x| ... )
- beidenmove
Elementenmy_vec
in den...
Geltungsbereich.Wenn Sie nur die Daten "betrachten" müssen, verwenden Sie
iter
, wenn Sie sie bearbeiten / mutieren müssen, verwenden Sieiter_mut
und wenn Sie ihr einen neuen Eigentümer geben müssen, verwenden Sieinto_iter
.Dies war hilfreich: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Machen Sie dies zu einem Community-Wiki, damit hoffentlich ein Rust-Profi diese Antwort bearbeiten kann, wenn ich Fehler gemacht habe.
quelle
iter
und artikuliertinto_iter
..into_iter()
wird nicht für ein Array selbst implementiert, sondern nur&[]
. Vergleichen Sie:mit
Da
IntoIterator
nur auf definiert ist&[T]
, kann das Slice selbst nicht auf die gleiche Weise gelöscht werden, wieVec
wenn Sie die Werte verwenden. (Werte können nicht verschoben werden)Warum das so ist, ist ein anderes Thema, und ich würde es gerne selbst lernen. Spekulieren: Array sind die Daten selbst, Slice ist nur ein Blick hinein. In der Praxis können Sie das Array nicht als Wert in eine andere Funktion verschieben, sondern nur eine Ansicht davon übergeben, sodass Sie es dort auch nicht verwenden können.
quelle
IntoIterator
auch umgesetzt wird&'a mut [T]
, so dass es könnte die Objekte aus der Anordnung bewegen. Ich denke, dass es mit der Tatsache zusammenhängt, dass die RückgabestrukturIntoIter<T>
währenddessenIter<'a, T>
kein lebenslanges Argument hat, so dass die erstere kein Slice enthalten kann.mut
bedeutet, dass Sie die Werte ändern können, nicht, dass Sie sie verschieben können.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "Fehler: kann nicht aus geliehenen InhaltenArrayIntoIter
Struktur mit unsicherem Rust als Teil der Bibliothek zu implementieren ... Vielleicht lohnt es sich nicht, wie Sie esVec
sowieso für diese Fälle verwenden sollten.array.into_iter
zurückkehrt&T
- weil es magisch ist, es automatisch umzuwandeln&array.into_iter
- und wenn ja, verstehe ich nicht, was das mit sich bewegenden Werten oder nicht bewegten Werten zu tun hat. Oder ist es, wie @rodrigo sagte, dass Sie die Referenz einfach erhalten, weil Sie (aus irgendeinem Grund) keine Werte aus Arrays verschieben können ? Immer noch sehr verwirrt.Ich denke, es gibt noch etwas zu klären. Auflistungstypen wie
Vec<T>
undVecDeque<T>
haben eineinto_iter
Methode, die sich ergibt,T
weil sie implementiert werdenIntoIterator<Item=T>
. Nichts hindert uns daran, einen Typ zu erstellen, der,Foo<T>
wenn er wiederholt wird,T
nur einen anderen Typ ergibtU
. Das heißt,Foo<T>
implementiertIntoIterator<Item=U>
.In der Tat gibt es einige Beispiele in
std
:&Path
GeräteIntoIterator<Item=&OsStr>
und&UnixListener
GeräteIntoIterator<Item=Result<UnixStream>>
.Der Unterschied zwischen
into_iter
unditer
Zurück zur ursprünglichen Frage zum Unterschied zwischen
into_iter
unditer
. Ähnlich wie andere darauf hingewiesen haben, besteht der Unterschied darin, dassinto_iter
es sich um eine erforderliche Methode handelt,IntoIterator
die jeden in angegebenen Typ ergeben kannIntoIterator::Item
. Typischerweise wird , wenn ein Typ implementiertIntoIterator<Item=I>
, durch Konvention es auch zwei Ad-hoc - Methoden hat:iter
unditer_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:Wir können jedoch nicht * ein Merkmal verwenden verpflichtet , eine Art zu verlangen, haben
iter
Verfahren oderiter_mut
Verfahren, weil sie nur Konventionen sind. Wir können sagen, dass diesinto_iter
weiter verbreitet ist alsiter
oderiter_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>
beistd
denen eineiter
Methode vorhanden ist, auch unveränderliche Referenztypen&SomeCollection<T>
implementiertIntoIterator<Item=&T>
. Zum Beispiel&Vec<T>
implementiertIntoIterator<Item=&T>
, so dass wir wiederholen können&Vec<T>
:Wenn
v.iter()
dies&v
in beiden Implementierungen gleichwertig istIntoIterator<Item=&T>
, warum stellt Rust dann beide bereit? Es ist für die Ergonomie. Infor
Schleifen ist die Verwendung etwas prägnanter&v
alsv.iter()
; aber in anderen Fällenv.iter()
ist viel klarer als(&v).into_iter()
:Ebenso kann in
for
Schleifenv.iter_mut()
ersetzt werden durch&mut v
:Wann (implementiert)
into_iter
unditer
Methoden für einen TypWenn 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 wederinto_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 Methodenbytes
bereitgestellt : zum Iterieren der Bytes undchars
zum Iterieren der Zeichen als Alternative zuriter
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 Typenstd
bereits implementiertIntoIterator
.quelle