rust - ¿Cuál es la diferencia entre iter e into_iter?
(2)
La primera pregunta es: "¿Qué es
into_iter
?"
into_iter
proviene del
rasgo
IntoIterator
:
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 implementa
IntoIterator
, puede usarse en un bucle
for
.
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 dentro de
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> { /* ... */ } }
Asi que:
¿Cuál es la diferencia entre
iter
einto_iter
?
into_iter
es un método genérico para obtener un iterador, ya sea que este iterador produzca valores, referencias inmutables o referencias mutables
depende del contexto
y a veces puede ser sorprendente.
iter
e
iter_mut
son métodos ad-hoc.
Esto funciona alrededor del bit dependiente del contexto y, por convención, le permite obtener un iterador que producirá referencias.
El autor de la publicación Rust by Example ilustra la sorpresa que viene de la dependencia del contexto (es decir, el tipo) en el que se llama
into_iter
, y también agrava el problema al usar el hecho de que:
-
IntoIterator
no está implementado para[T; N]
[T; N]
, solo para&[T; N]
&[T; N]
y&mut [T; N]
&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 para
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é los arreglos implementan
IntoIterator
(de una manera tan sorprendente): es para hacer posible iterar sobre las referencias a ellos en bucles.
Estoy haciendo el tutorial Rust by Example que tiene este fragmento de código:
// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
Estoy completamente confundido: para un
Vec
el iterador devuelto por
iter
produce referencias y el iterador devuelto por
into_iter
produce valores, pero para una matriz, estos iteradores son idénticos.
¿Cuál es el caso de uso / API para estos dos métodos?
.into_iter()
no está implementado para una matriz en sí, sino solo
&[]
.
Comparar:
impl<''a, T> IntoIterator for &''a [T]
type Item = &''a T
con
impl<T> IntoIterator for Vec<T>
type Item = T
Dado que
IntoIterator
se define solo en
&[T]
, el segmento en sí no se puede descartar de la misma manera que
Vec
cuando utiliza 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 es la información en sí misma, 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í.