TL; DR:
- Iterator zwrócony przez
into_iter
mogą dawać każdy z T
, &T
albo &mut T
, w zależności od kontekstu.
- Iterator zwrócony przez
iter
zwróci &T
zgodnie z konwencją.
- Iterator zwrócony przez
iter_mut
zwróci &mut T
zgodnie z konwencją.
Pierwsze pytanie brzmi: „Co to jest into_iter
?”
into_iter
pochodzi z IntoIterator
cechy :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Tę cechę implementujesz, gdy chcesz określić, w jaki sposób dany typ ma zostać przekształcony w iterator. Przede wszystkim, jeśli typ implementuje IntoIterator
, może być używany w for
pętli.
Na przykład Vec
wdraża IntoIterator
... trzykrotnie!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Każdy wariant jest nieco inny.
Ten zużywa the, Vec
a jego iterator zwraca wartości ( T
bezpośrednio):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Pozostałe dwa pobierają wektor przez odniesienie (nie dajcie się zwieść podpisowi, into_iter(self)
ponieważ self
jest to odniesienie w obu przypadkach), a ich iteratory wytworzą odniesienia do elementów wewnątrz Vec
.
Ten daje niezmienne odwołania :
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> { /* ... */ }
}
Chociaż ten daje zmienne odniesienia :
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> { /* ... */ }
}
Więc:
Jaka jest różnica między iter
i into_iter
?
into_iter
jest ogólną metodą uzyskiwania iteratora, niezależnie od tego, czy ten iterator zwraca wartości, niezmienne odwołania, czy zmienne odwołania, zależy od kontekstu i czasami może być zaskakujące.
iter
i iter_mut
są metodami ad hoc. Ich typ zwracany jest zatem niezależny od kontekstu i zwykle będą iteratorami dającymi odpowiednio niezmienne i zmienne odwołania.
Autor postu Rust by Example ilustruje zaskoczenie wynikające z zależności od kontekstu (tj. Typu), od którego into_iter
jest wywoływany, a także potęguje problem, wykorzystując fakt, że:
IntoIterator
nie jest zaimplementowana dla [T; N]
, tylko dla &[T; N]
i&mut [T; N]
- Gdy metoda nie jest zaimplementowana dla wartości, zamiast tego jest automatycznie przeszukiwana pod kątem odniesień do tej wartości
co jest bardzo zaskakujące, into_iter
ponieważ wszystkie typy (z wyjątkiem [T; N]
) implementują go dla wszystkich 3 odmian (wartość i odwołania). Tablica nie może zaimplementować iteratora, który zwraca wartości, ponieważ nie może „zmniejszyć”, aby zrezygnować ze swoich elementów.
A dlaczego tablice implementują się IntoIterator
(w tak zaskakujący sposób): ma to umożliwić iterację po odwołaniach do nich w for
pętlach.
into_iter
wybiera implementację na podstawie tego, czy odbiornik jest wartością, referencją czy zmienną referencją. (2) W Rust nie ma zmiennych wartości, a raczej każda wartość jest zmienna, ponieważ masz prawo własności.&'a MyStruct
a&mut 'a MyStruct
i pierwszy został wybrany, jeśli zawsze obecne, nawet jeśli zadzwoniłeminto_iter().for_each()
namut
wartości z&mut
argumentów lambda.Ja (nowicjusz w Rust) przyszedłem tutaj z Google, szukając prostej odpowiedzi, której nie dostarczyły inne odpowiedzi. Oto prosta odpowiedź:
iter()
iteruje po elementach przez odniesienieinto_iter()
iteruje po elementach, przenosząc je do nowego zakresuiter_mut()
iteruje po elementach, podając zmienne odniesienie do każdego elementuWięc
for x in my_vec { ... }
jest zasadniczo równoważnemy_vec.into_iter().for_each(|x| ... )
- obumove
elementommy_vec
do...
zakresu.Jeśli potrzebujesz tylko „spojrzeć” na dane, użyj
iter
, jeśli chcesz je edytować / zmutować, użyjiter_mut
, a jeśli chcesz nadać im nowego właściciela, użyjinto_iter
.To było pomocne: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Uczynienie z tego wiki społeczności, aby miejmy nadzieję, że profesjonalista Rust mógł edytować tę odpowiedź, jeśli popełniłem jakieś błędy.
źródło
iter
ainto_iter
..into_iter()
nie jest zaimplementowana dla samej tablicy, ale tylko&[]
. Porównać:z
Ponieważ
IntoIterator
jest zdefiniowane tylko na&[T]
, sam plaster nie może zostać usunięty w taki sam sposób, jakVec
podczas używania wartości. (wartości nie mogą zostać przeniesione)Dlaczego tak jest, to inna kwestia i chciałbym się nauczyć. Spekulowanie: tablica to same dane, wycinek to tylko widok do niej. W praktyce nie możesz przenieść tablicy jako wartości do innej funkcji, po prostu przekazać jej widok, więc nie możesz jej również tam skonsumować.
źródło
IntoIterator
jest również zaimplementowany dla&'a mut [T]
, więc może przenosić obiekty z tablicy. Myślę, że jest to związane z faktem, że struktura returnIntoIter<T>
nie ma argumentu dożywotniego, podczas gdyIter<'a, T>
ma, więc ta pierwsza nie może utrzymać kawałka.mut
oznacza, że możesz zmieniać wartości, a nie przenosić je.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "błąd: nie można wyprowadzić się z wypożyczonych treści"ArrayIntoIter
struktury przy użyciu niebezpiecznego Rusta, jako części biblioteki ... Może nie warto, bo i tak powinieneś używaćVec
w takich przypadkach.array.into_iter
powraca&T
- ponieważ robi magię, aby automatycznie przekształcić ją w&array.into_iter
- a jeśli tak, nie rozumiem, co to ma wspólnego z przenoszeniem wartości lub ich nieporuszaniem. Czy jest tak, jak powiedział @rodrigo, że otrzymujesz odniesienie tylko dlatego, że (z jakiegoś powodu) nie możesz przenieść wartości z tablic ? Wciąż bardzo zdezorientowany.Myślę, że jest coś do wyjaśnienia. Typy kolekcji, takie jak
Vec<T>
iVecDeque<T>
, mająinto_iter
metodę, która daje wynik,T
ponieważ implementująIntoIterator<Item=T>
. Nie ma nic, co mogłoby nas powstrzymać przed utworzeniem typu,Foo<T>
jeśli który zostanie powtórzony, przyniesie nieT
tylko inny typU
. To znaczyFoo<T>
narzędziaIntoIterator<Item=U>
.W rzeczywistości jest kilka przykładów w
std
:&Path
narzędziaIntoIterator<Item=&OsStr>
i&UnixListener
narzędziaIntoIterator<Item=Result<UnixStream>>
.Różnica między
into_iter
iiter
Wracając do pierwotnego pytania o różnicę między
into_iter
aiter
. Podobnie jak inni wskazywali, różnica polega na tym, żeinto_iter
jest to wymagana metoda,IntoIterator
która może dać dowolny typ określony wIntoIterator::Item
. Zazwyczaj, jeśli typ implementujeIntoIterator<Item=I>
, zgodnie z konwencją ma również dwie metody ad-hoc:iter
i,iter_mut
które dają&I
i&mut I
, odpowiednio.Oznacza to, że możemy stworzyć funkcję, która otrzyma typ, który ma
into_iter
metodę (tj. Jest iterowalny), używając powiązanej cechy:Jednak nie możemy * użyć cechy związanej z wymaganiem, aby typ miał
iter
metodę lubiter_mut
metodę, ponieważ są to tylko konwencje. Można powiedzieć, żeinto_iter
ma szersze zastosowanie niżiter
lubiter_mut
.Alternatywy dla
iter
iiter_mut
Inną ciekawą rzeczą
iter
jest to, że nie jest to jedyny sposób na uzyskanie iteratora, który daje wyniki&T
. Zgodnie z konwencją (ponownie) typy kolekcji,SomeCollection<T>
wstd
których mająiter
metodę, również mają&SomeCollection<T>
implementację niezmiennych typów referencyjnychIntoIterator<Item=&T>
. Na przykład&Vec<T>
implementujeIntoIterator<Item=&T>
, więc pozwala nam iterować po&Vec<T>
:Jeśli
v.iter()
jest równoważne&v
w obu implementacjachIntoIterator<Item=&T>
, dlaczego więc Rust zapewnia oba? Chodzi o ergonomię. Wfor
pętlach jest to nieco bardziej zwięzłe w użyciu&v
niżv.iter()
; ale w innych przypadkachv.iter()
jest dużo jaśniejszy niż(&v).into_iter()
:Podobnie w
for
pętlachv.iter_mut()
można zastąpić&mut v
:Kiedy podać (zaimplementować)
into_iter
iiter
metody dla typuJeśli typ ma tylko jedną „drogę” do iteracji, powinniśmy zaimplementować obie. Jeśli jednak istnieją dwa lub więcej sposobów, które można powtórzyć, powinniśmy zamiast tego zapewnić metodę ad hoc dla każdej z nich.
Na przykład
String
zapewnia ani,into_iter
ani,iter
ponieważ istnieją dwa sposoby na iterację: iteracja jej reprezentacji w bajtach lub iteracja jej reprezentacji w znakach. Zamiast tego udostępnia dwie metody:bytes
do iteracji bajtów ichars
do iteracji znaków, jako alternatywę dlaiter
metody.* Cóż, technicznie możemy to zrobić, tworząc cechę. Ale potem potrzebujemy
impl
tej cechy dla każdego typu, którego chcemy użyć. Tymczasem wiele typów jeststd
już wdrożonychIntoIterator
.źródło