TL; DR:
- El iterador devuelto por
into_iter
puede producir cualquiera de T
, &T
o &mut T
, dependiendo del contexto.
- El iterador devuelto por
iter
rendirá &T
, por convención.
- El iterador devuelto por
iter_mut
rendirá &mut T
, por convención.
La primera pregunta es: "¿Qué es into_iter
?"
into_iter
proviene del IntoIterator
rasgo :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Implementa este rasgo cuando desea especificar cómo se convertirá un tipo particular en un iterador. En particular, si un tipo lo implementa IntoIterator
, puede usarse en un for
bucle.
Por ejemplo, Vec
implementa IntoIterator
... ¡tres veces!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Cada variante es ligeramente diferente.
Este consume el Vec
y su iterador produce valores ( T
directamente):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Los otros dos toman el vector por referencia (no se deje engañar por la firma de into_iter(self)
porque self
es una referencia en ambos casos) y sus iteradores producirán referencias a los elementos en su interior Vec
.
Este produce referencias inmutables :
impl<'a, T> IntoIterator for &'a Vec<T> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}
Si bien este produce referencias mutables :
impl<'a, T> IntoIterator for &'a mut Vec<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}
Entonces:
¿Cuál es la diferencia entre iter
y into_iter
?
into_iter
es un método genérico para obtener un iterador, si este iterador produce valores, referencias inmutables o referencias mutables depende del contexto y a veces puede ser sorprendente.
iter
y iter_mut
son métodos ad-hoc. Su tipo de retorno es, por lo tanto, independiente del contexto, y convencionalmente serán iteradores que producirán referencias inmutables y referencias mutables, respectivamente.
El autor de la publicación Rust by Example ilustra la sorpresa que viene de la dependencia del contexto (es decir, el tipo) al que into_iter
se llama, y también agrava el problema al usar el hecho de que:
IntoIterator
no está implementado para [T; N]
, solo para &[T; N]
y&mut [T; N]
- Cuando un método no se implementa para un valor, se busca automáticamente referencias a ese valor
lo cual es muy sorprendente into_iter
ya que todos los tipos (excepto [T; N]
) lo implementan para las 3 variaciones (valor y referencias). No es posible que la matriz implemente un iterador que arroje valores porque no puede "reducir" para renunciar a sus elementos.
En cuanto a por qué se implementan las matrices IntoIterator
(de una manera tan sorprendente): es para hacer posible iterar sobre las referencias a ellas en for
bucles.
into_iter
elige una implementación en función de si el receptor es un valor, referencia o referencia mutable. (2) No hay valores mutables en Rust, o más bien, cualquier valor es mutable ya que usted tiene la propiedad.&'a MyStruct
y,&mut 'a MyStruct
y el primero siempre se elegía si estaba presente, incluso si invoquéinto_iter().for_each()
elmut
valor con&mut
argumentos en lambda.Yo (un novato de Rust) vine de Google buscando una respuesta simple que no fue proporcionada por las otras respuestas. Aquí está esa respuesta simple:
iter()
itera sobre los elementos por referenciainto_iter()
itera sobre los elementos, moviéndolos al nuevo alcanceiter_mut()
itera sobre los elementos, dando una referencia mutable a cada elementoAsí que
for x in my_vec { ... }
es esencialmente equivalente amy_vec.into_iter().for_each(|x| ... )
- tantomove
los elementos demy_vec
en el...
ámbito de aplicación.Si solo necesita "mirar" los datos, use
iter
, si necesita editarlos / mutarlos, useiter_mut
, y si necesita darle un nuevo propietario, useinto_iter
.Esto fue útil: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Hacer de este un wiki de la comunidad para que un profesional de Rust pueda editar esta respuesta si cometí algún error
fuente
iter
yinto_iter
..into_iter()
no se implementa para una matriz en sí, sino solo&[]
. Comparar:con
Como
IntoIterator
se define solo en&[T]
, el segmento en sí no se puede descartar de la misma manera queVec
cuando usa los valores. (los valores no se pueden mover)Ahora, por qué ese es el caso es un problema diferente, y me gustaría aprender yo mismo. Especulando: la matriz son los datos en sí, el segmento es solo una vista. En la práctica, no puede mover la matriz como valor a otra función, simplemente pasar una vista de ella, por lo que tampoco puede consumirla allí.
fuente
IntoIterator
también está implementado para&'a mut [T]
, por lo que podría mover los objetos fuera de la matriz. Creo que está relacionado con el hecho de que la estructura de retornoIntoIter<T>
no tiene un argumento de por vida mientras queIter<'a, T>
sí, por lo que el primero no puede contener un segmento.mut
significa que puede cambiar los valores, no que puede moverlos.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "error: no se puede salir del contenido prestado"ArrayIntoIter
estructura utilizando Rust inseguro, como parte de la biblioteca ... Tal vez no valga la pena, ya que deVec
todos modos debería usarlo para esos casos.array.into_iter
regresa&T
, porque está haciendo magia convertirlo automáticamente a&array.into_iter
, y si es así, no entiendo qué tiene que ver con valores móviles o no valores móviles. ¿O es como dijo @rodrigo, que obtienes la referencia simplemente porque (por alguna razón) no puedes mover valores fuera de las matrices ? Aún muy confundido.Creo que hay algo que aclarar un poco más. Los tipos de colección, como
Vec<T>
yVecDeque<T>
, tienen uninto_iter
método que rindeT
porque se implementanIntoIterator<Item=T>
. No hay nada que nos detenga para crear un tipoFoo<T>
si se repite, no daráT
sino otro tipoU
. Es decir,Foo<T>
implementosIntoIterator<Item=U>
.De hecho, hay algunos ejemplos en
std
:&Path
implementosIntoIterator<Item=&OsStr>
e&UnixListener
implementosIntoIterator<Item=Result<UnixStream>>
.La diferencia entre
into_iter
yiter
Volviendo a la pregunta original sobre la diferencia entre
into_iter
yiter
. Similar a lo que otros han señalado, la diferencia es queinto_iter
es un método requeridoIntoIterator
que puede producir cualquier tipo especificado enIntoIterator::Item
. Típicamente, si un tipo se implementaIntoIterator<Item=I>
, por convención también tiene dos métodos ad-hoc:iter
yiter_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:Sin embargo, no podemos * usar un rasgo obligado a requerir que un tipo tenga un
iter
método oiter_mut
método, porque son solo convenciones. Podemos decir queinto_iter
es más ampliamente utilizable queiter
oiter_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ónSomeCollection<T>
en losstd
que tieneniter
método también tienen&SomeCollection<T>
implementados sus tipos de referencia inmutablesIntoIterator<Item=&T>
. Por ejemplo,&Vec<T>
implementaIntoIterator<Item=&T>
, por lo que nos permite iterar sobre&Vec<T>
:Si
v.iter()
es equivalente a&v
que ambos implementosIntoIterator<Item=&T>
, ¿por qué Rust proporciona ambos? Es para la ergonomía. Enfor
bucles, es un poco más conciso de usar&v
quev.iter()
; pero en otros casos,v.iter()
es mucho más claro que(&v).into_iter()
:Del mismo modo, en
for
bucles,v.iter_mut()
se puede reemplazar con&mut v
:Cuándo proporcionar (implementar)
into_iter
yiter
métodos para un tipoSi 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 proporcionainto_iter
niiter
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 ychars
para iterar los caracteres, como alternativas aliter
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 tiposstd
ya se implementanIntoIterator
.fuente