Myślę, że jest coś do wyjaśnienia. Typy kolekcji, takie jak Vec<T>
i VecDeque<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 nie T
tylko inny typ U
. To znaczy Foo<T>
narzędzia IntoIterator<Item=U>
.
W rzeczywistości jest kilka przykładów w std
: &Path
narzędzia IntoIterator<Item=&OsStr>
i &UnixListener
narzędzia IntoIterator<Item=Result<UnixStream>>
.
Różnica między into_iter
iiter
Wracając do pierwotnego pytania o różnicę między into_iter
a iter
. Podobnie jak inni wskazywali, różnica polega na tym, że into_iter
jest to wymagana metoda, IntoIterator
która może dać dowolny typ określony w IntoIterator::Item
. Zazwyczaj, jeśli typ implementuje IntoIterator<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:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Jednak nie możemy * użyć cechy związanej z wymaganiem, aby typ miał iter
metodę lub iter_mut
metodę, ponieważ są to tylko konwencje. Można powiedzieć, że into_iter
ma szersze zastosowanie niż iter
lub iter_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>
w std
których mają iter
metodę, również mają &SomeCollection<T>
implementację niezmiennych typów referencyjnych IntoIterator<Item=&T>
. Na przykład &Vec<T>
implementuje IntoIterator<Item=&T>
, więc pozwala nam iterować po &Vec<T>
:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
Jeśli v.iter()
jest równoważne &v
w obu implementacjach IntoIterator<Item=&T>
, dlaczego więc Rust zapewnia oba? Chodzi o ergonomię. W for
pętlach jest to nieco bardziej zwięzłe w użyciu &v
niż v.iter()
; ale w innych przypadkach v.iter()
jest dużo jaśniejszy niż (&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();
Podobnie w for
pętlach v.iter_mut()
można zastąpić &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;
}
Kiedy podać (zaimplementować) into_iter
i iter
metody dla typu
Jeś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 i chars
do iteracji znaków, jako alternatywę dla iter
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 jest std
już wdrożonych IntoIterator
.