Creo que hay algo que aclarar un poco más. Los tipos de colección, como Vec<T>
y VecDeque<T>
, tienen un into_iter
método que rinde T
porque se implementan IntoIterator<Item=T>
. No hay nada que nos detenga para crear un tipo Foo<T>
si se repite, no dará T
sino otro tipo U
. Es decir, Foo<T>
implementos IntoIterator<Item=U>
.
De hecho, hay algunos ejemplos en std
: &Path
implementos IntoIterator<Item=&OsStr>
e &UnixListener
implementos IntoIterator<Item=Result<UnixStream>>
.
La diferencia entre into_iter
yiter
Volviendo a la pregunta original sobre la diferencia entre into_iter
y iter
. Similar a lo que otros han señalado, la diferencia es que into_iter
es un método requerido IntoIterator
que puede producir cualquier tipo especificado en IntoIterator::Item
. Típicamente, si un tipo se implementa IntoIterator<Item=I>
, por convención también tiene dos métodos ad-hoc: iter
y iter_mut
que rinden &I
y &mut I
, respectivamente.
Lo que implica es que podemos crear una función que recibe un tipo que tiene un into_iter
método (es decir, es iterable) mediante el uso de un límite de rasgo:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Sin embargo, no podemos * usar un rasgo obligado a requerir que un tipo tenga un iter
método o iter_mut
método, porque son solo convenciones. Podemos decir que into_iter
es más ampliamente utilizable que iter
o iter_mut
.
Alternativas a iter
yiter_mut
Otro iter
aspecto interesante para observar es que no es la única forma de obtener un iterador que rinda &T
. Por convención (nuevamente), los tipos de colección SomeCollection<T>
en los std
que tienen iter
método también tienen &SomeCollection<T>
implementados sus tipos de referencia inmutables IntoIterator<Item=&T>
. Por ejemplo, &Vec<T>
implementa IntoIterator<Item=&T>
, por lo que nos 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);
}
Si v.iter()
es equivalente a &v
que ambos implementos IntoIterator<Item=&T>
, ¿por qué Rust proporciona ambos? Es para la ergonomía. En for
bucles, es un poco más conciso de usar &v
que v.iter()
; pero en otros casos, v.iter()
es mucho más 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();
Del mismo modo, en for
bucles, v.iter_mut()
se puede reemplazar 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;
}
Cuándo proporcionar (implementar) into_iter
y iter
métodos para un tipo
Si el tipo solo tiene una "forma" para iterar, deberíamos implementar ambas. Sin embargo, si hay dos formas o más de que se puede repetir, deberíamos proporcionar un método ad-hoc para cada forma.
Por ejemplo, String
no proporciona into_iter
ni iter
porque hay dos formas de iterarlo: iterar su representación en bytes o iterar su representación en caracteres. En cambio, proporciona dos métodos: bytes
para iterar los bytes y chars
para iterar los caracteres, como alternativas al iter
método.
* Bueno, técnicamente podemos hacerlo creando un rasgo. Pero entonces necesitamos impl
ese rasgo para cada tipo que queremos usar. Mientras tanto, muchos tipos std
ya se implementan IntoIterator
.