web-dev-qa-db-fra.com

Quelle est la difference entre iter et into_iter?

Je fais le tutoriel Rust by Example qui contient cet extrait de code:

// 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));

Je suis complètement confus - pour un Vec, l'itérateur retourné de iter donne des références et l'itérateur est revenu de into_iter donne des valeurs, mais pour un tableau, ces itérateurs sont identiques?

Quel est le cas d'utilisation/API pour ces deux méthodes?

94
vitiral

La première question est: "Qu'est-ce que into_iter?"

into_iter Provient du IntoIterator trait :

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

Vous implémentez cette caractéristique lorsque vous souhaitez spécifier comment un type particulier doit être converti en itérateur. Plus particulièrement, si un type implémente IntoIterator, il peut être utilisé dans une boucle for.

Par exemple, Vec implémente IntoIterator... trois fois!

impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>

Chaque variante est légèrement différente.

Celui-ci consomme le Vec et son itérateur donne les valeurs (T directement):

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}

Les deux autres prennent le vecteur par référence (ne vous fiez pas à la signature de into_iter(self), car self est une référence dans les deux cas) et leurs itérateurs produiront des références aux éléments à l'intérieur de Vec.

Celui-ci donne des références immuables :

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> { /* ... */ }
}

Alors que celui-ci donne des références 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> { /* ... */ }
}

Alors:

Quelle est la différence entre iter et into_iter?

into_iter Est une méthode générique pour obtenir un itérateur, que cet itérateur renvoie des valeurs, des références immuables ou des références mutables dépend du contexte et peut parfois être surprenant.

iter et iter_mut sont des méthodes ad hoc. Cela fonctionne autour du bit dépendant du contexte et, par convention, vous permet d'obtenir un itérateur qui générera des références.

L'auteur de l'article Rust by Example) illustre la surprise découlant de la dépendance au contexte (c'est-à-dire le type) sur lequel into_iter Est appelé et aggrave également le problème. en utilisant le fait que:

  1. IntoIterator n'est pas implémenté pour [T; N], uniquement pour &[T; N] et &mut [T; N]
  2. Lorsqu'une méthode n'est pas implémentée pour une valeur, elle est automatiquement recherchée références à cette valeur

ce qui est très surprenant pour into_iter puisque tous les types (sauf [T; N]) l'implémentent pour les 3 variations (valeur et références). Il n'est pas possible pour le tableau d'implémenter un itérateur qui produit des valeurs car il ne peut pas "réduire" ses éléments.

Quant aux raisons pour lesquelles les tableaux implémentent IntoIterator (d’une manière aussi surprenante): c’est pour rendre possible la répétition de leurs itérations dans des boucles for.

84
Matthieu M.

.into_iter() n'est pas implémenté pour un tableau, mais seulement &[]. Comparer:

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T

avec

impl<T> IntoIterator for Vec<T>
    type Item = T

Puisque IntoIterator est défini uniquement sur &[T], la tranche elle-même ne peut pas être supprimée de la même manière que Vec lorsque vous utilisez les valeurs. (les valeurs ne peuvent pas être déplacées)

Maintenant, la raison pour laquelle c'est le cas est un problème différent, et j'aimerais apprendre moi-même. Spéculation: le tableau est la donnée elle-même, la tranche n’est qu’une vue. En pratique, vous ne pouvez pas déplacer le tableau en tant que valeur dans une autre fonction, mais simplement en passer un aperçu afin que vous ne puissiez pas le consommer là non plus.

5
viraptor

Je (un Rust)) est venu ici de Google à la recherche d'une réponse simple, qui n'était pas fournie par les autres réponses. Voici cette réponse simple:

  • iter() itère les éléments par référence
  • into_iter() effectue une itération sur les éléments, les déplaçant dans la nouvelle portée
  • iter_mut() itère sur les éléments en donnant une référence modifiable à chaque élément

Donc for x in my_vec { ... } Est essentiellement équivalent à my_vec.into_iter().for_each(|x| ... ) - à la fois move aux éléments de my_vec Dans la portée ....

Si vous avez juste besoin de "regarder" les données, utilisez iter, si vous devez les éditer/les transformer, utilisez iter_mut, Et si vous avez besoin de lui donner un nouveau propriétaire, utilisez into_iter.

Cela a été utile: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-Rust.html

Faire de ce wiki une communauté afin que Rust pro) puisse éditer cette réponse si j'ai commis des erreurs.

1
joe