Myślę, że jest coś do wyjaśnienia. Typy kolekcji, takie jak Vec<T>i VecDeque<T>, mają into_itermetodę, która daje wynik, Tponieważ 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 Ttylko 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_iteriiter
Wracając do pierwotnego pytania o różnicę między into_itera iter. Podobnie jak inni wskazywali, różnica polega na tym, że into_iterjest to wymagana metoda, IntoIteratorktó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: iteri, iter_mutktóre dają &Ii &mut I, odpowiednio.
Oznacza to, że możemy stworzyć funkcję, która otrzyma typ, który ma into_itermetodę (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ł itermetodę lub iter_mutmetodę, ponieważ są to tylko konwencje. Można powiedzieć, że into_iterma szersze zastosowanie niż iterlub iter_mut.
Alternatywy dla iteriiter_mut
Inną ciekawą rzeczą iterjest 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 stdktórych mają itermetodę, 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 &vw obu implementacjach IntoIterator<Item=&T>, dlaczego więc Rust zapewnia oba? Chodzi o ergonomię. W forpętlach jest to nieco bardziej zwięzłe w użyciu &vniż 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 forpę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_iteri itermetody 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 Stringzapewnia ani, into_iterani, iterponieważ istnieją dwa sposoby na iterację: iteracja jej reprezentacji w bajtach lub iteracja jej reprezentacji w znakach. Zamiast tego udostępnia dwie metody: bytesdo iteracji bajtów i charsdo iteracji znaków, jako alternatywę dla itermetody.
* Cóż, technicznie możemy to zrobić, tworząc cechę. Ale potem potrzebujemy impltej cechy dla każdego typu, którego chcemy użyć. Tymczasem wiele typów jest stdjuż wdrożonych IntoIterator.