Eu acho que há algo para esclarecer um pouco mais. Tipos de coleção, como Vec<T>
e VecDeque<T>
, têm um into_iter
método que gera T
porque eles implementam IntoIterator<Item=T>
. Não há nada para nos impedir de criar um tipo, Foo<T>
se for iterado, ele produzirá não T
apenas outro tipo U
. Ou seja, Foo<T>
implementa IntoIterator<Item=U>
.
De fato, existem alguns exemplos em std
: &Path
implementos IntoIterator<Item=&OsStr>
e &UnixListener
implementos IntoIterator<Item=Result<UnixStream>>
.
A diferença entre into_iter
eiter
Voltar à pergunta original sobre a diferença entre into_iter
e iter
. Semelhante ao que outros já apontaram, a diferença é que into_iter
é um método exigido IntoIterator
que pode gerar qualquer tipo especificado em IntoIterator::Item
. Normalmente, se um tipo é implementado IntoIterator<Item=I>
, por convenção, ele também possui dois métodos ad-hoc: iter
e iter_mut
que produzem &I
e &mut I
, respectivamente.
O que isso implica é que podemos criar uma função que recebe um tipo que possui into_iter
método (ou seja, é iterável) usando um atributo vinculado:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
No entanto, não podemos * usar uma característica vinculada para exigir que um tipo tenha iter
método ou iter_mut
método, porque são apenas convenções. Podemos dizer que into_iter
é mais amplamente utilizável que iter
ou iter_mut
.
Alternativas iter
eiter_mut
Outro interessante a observar é que iter
não é a única maneira de obter um iterador que produza &T
. Por convenção (novamente), os tipos de coleção SomeCollection<T>
nos std
quais o iter
método também tem seus tipos de referência imutáveis &SomeCollection<T>
implementados IntoIterator<Item=&T>
. Por exemplo, &Vec<T>
implementa IntoIterator<Item=&T>
, portanto, permite iterar sobre &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
que ambos implementam IntoIterator<Item=&T>
, por que então Rust fornece ambos? É para ergonomia. Em for
loops, é um pouco mais conciso do &v
que v.iter()
; mas em outros casos, v.iter()
é muito mais claro que (&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();
Da mesma forma, em for
loops, v.iter_mut()
pode ser substituído por &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 fornecer (implementar) into_iter
e iter
métodos para um tipo
Se o tipo tiver apenas uma "maneira" de ser repetida, devemos implementar as duas. No entanto, se há duas maneiras ou mais para iterar, devemos fornecer um método ad-hoc para cada maneira.
Por exemplo, String
fornece nem into_iter
nem iter
porque existem duas maneiras de iterá-lo: iterar sua representação em bytes ou iterar sua representação em caracteres. Em vez disso, fornece dois métodos: bytes
para iterar os bytes e chars
para iterar os caracteres, como alternativas ao iter
método.
* Bem, tecnicamente, podemos fazer isso criando uma característica. Mas então precisamos impl
dessa característica para cada tipo que queremos usar. Enquanto isso, muitos tipos std
já implementam IntoIterator
.