Penso che ci sia qualcosa da chiarire un po 'di più. I tipi di raccolta, come Vec<T>
e VecDeque<T>
, hanno un into_iter
metodo che produce T
perché implementano IntoIterator<Item=T>
. Non c'è nulla che ci impedisca di creare un tipo Foo<T>
se è ripetuto, non produrrà T
altro tipo U
. Cioè, Foo<T>
implementa IntoIterator<Item=U>
.
In effetti, ci sono alcuni esempi in std
: &Path
attrezzi IntoIterator<Item=&OsStr>
e &UnixListener
attrezzi IntoIterator<Item=Result<UnixStream>>
.
La differenza tra into_iter
eiter
Torna alla domanda originale sulla differenza tra into_iter
e iter
. Simile a quello che altri hanno sottolineato, la differenza è che into_iter
è un metodo richiesto IntoIterator
che può produrre qualsiasi tipo specificato in IntoIterator::Item
. Tipicamente, se un tipo implementa IntoIterator<Item=I>
, per convenzione ha anche due metodi ad-hoc: iter
e iter_mut
che producono &I
e &mut I
, rispettivamente.
Ciò che implica è che possiamo creare una funzione che riceve un tipo che ha un into_iter
metodo (cioè è un iterabile) usando un limite tratto:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Tuttavia, non possiamo * usare un tratto associato per richiedere che un tipo abbia un iter
metodo o un iter_mut
metodo, perché sono solo convenzioni. Possiamo dire che into_iter
è più ampiamente utilizzabile di iter
o iter_mut
.
Alternative a iter
eiter_mut
Un altro interessante da osservare è che iter
non è l'unico modo per ottenere un iteratore che cede &T
. Per convenzione (di nuovo), i tipi di raccolta SomeCollection<T>
in std
cui hanno iter
metodo hanno anche i loro tipi di riferimento immutabili &SomeCollection<T>
implementare IntoIterator<Item=&T>
. Ad esempio, &Vec<T>
implementa IntoIterator<Item=&T>
, quindi ci consente di iterare su &Vec<T>
:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
Se v.iter()
è equivalente a &v
quello in entrambi gli strumenti IntoIterator<Item=&T>
, perché Rust fornisce entrambi? È per l'ergonomia. Nei for
loop, è un po 'più conciso da usare &v
di v.iter()
; ma in altri casi, v.iter()
è molto più chiaro di (&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();
Allo stesso modo, in for
loop, v.iter_mut()
può essere sostituito con &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;
}
Quando fornire (implementare) into_iter
e iter
metodi per un tipo
Se il tipo ha un solo "modo" su cui iterare, dovremmo implementare entrambi. Tuttavia, se ci sono due o più modi in cui può essere ripetuto, dovremmo invece fornire un metodo ad hoc per ciascun modo.
Ad esempio, String
non fornisce né into_iter
né iter
perché ci sono due modi per iterarlo: iterare la sua rappresentazione in byte o iterare la sua rappresentazione in caratteri. Fornisce invece due metodi: bytes
per iterare i byte e chars
per iterare i caratteri, in alternativa al iter
metodo.
* Beh, tecnicamente possiamo farlo creando un tratto. Ma allora abbiamo bisogno di impl
quel tratto per ogni tipo che vogliamo usare. Nel frattempo, molti tipi std
già implementano IntoIterator
.