TL; DR:
- El iterador devuelto por
into_iterpuede producir cualquiera de T, &To &mut T, dependiendo del contexto.
- El iterador devuelto por
iterrendirá &T, por convención.
- El iterador devuelto por
iter_mutrendirá &mut T, por convención.
La primera pregunta es: "¿Qué es into_iter?"
into_iterproviene del IntoIteratorrasgo :
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 forbucle.
Por ejemplo, Vecimplementa 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 Vecy su iterador produce valores ( Tdirectamente):
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 selfes 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 itery into_iter?
into_iteres 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.
itery iter_mutson 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_iterse llama, y también agrava el problema al usar el hecho de que:
IntoIteratorno 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_iterya 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 forbucles.
into_iterelige 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 MyStructy,&mut 'a MyStructy el primero siempre se elegía si estaba presente, incluso si invoquéinto_iter().for_each()elmutvalor con&mutargumentos 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| ... )- tantomovelos elementos demy_vecen 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
iteryinto_iter..into_iter()no se implementa para una matriz en sí, sino solo&[]. Comparar:con
Como
IntoIteratorse define solo en&[T], el segmento en sí no se puede descartar de la misma manera queVeccuando 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
IntoIteratortambié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.mutsignifica 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"ArrayIntoIterestructura utilizando Rust inseguro, como parte de la biblioteca ... Tal vez no valga la pena, ya que deVectodos modos debería usarlo para esos casos.array.into_iterregresa&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_itermétodo que rindeTporque se implementanIntoIterator<Item=T>. No hay nada que nos detenga para crear un tipoFoo<T>si se repite, no daráTsino otro tipoU. Es decir,Foo<T>implementosIntoIterator<Item=U>.De hecho, hay algunos ejemplos en
std:&PathimplementosIntoIterator<Item=&OsStr>e&UnixListenerimplementosIntoIterator<Item=Result<UnixStream>>.La diferencia entre
into_iteryiterVolviendo a la pregunta original sobre la diferencia entre
into_iteryiter. Similar a lo que otros han señalado, la diferencia es queinto_iteres un método requeridoIntoIteratorque 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:iteryiter_mutque rinden&Iy&mut I, respectivamente.Lo que implica es que podemos crear una función que recibe un tipo que tiene un
into_itermé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
itermétodo oiter_mutmétodo, porque son solo convenciones. Podemos decir queinto_iteres más ampliamente utilizable queiteroiter_mut.Alternativas a
iteryiter_mutOtro
iteraspecto 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 losstdque tienenitermé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&vque ambos implementosIntoIterator<Item=&T>, ¿por qué Rust proporciona ambos? Es para la ergonomía. Enforbucles, es un poco más conciso de usar&vquev.iter(); pero en otros casos,v.iter()es mucho más claro que(&v).into_iter():Del mismo modo, en
forbucles,v.iter_mut()se puede reemplazar con&mut v:Cuándo proporcionar (implementar)
into_iteryitermé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,
Stringno proporcionainto_iterniiterporque hay dos formas de iterarlo: iterar su representación en bytes o iterar su representación en caracteres. En cambio, proporciona dos métodos:bytespara iterar los bytes ycharspara iterar los caracteres, como alternativas alitermétodo.* Bueno, técnicamente podemos hacerlo creando un rasgo. Pero entonces necesitamos
implese rasgo para cada tipo que queremos usar. Mientras tanto, muchos tiposstdya se implementanIntoIterator.fuente