TL; DR:
- L'itérateur renvoyé par
into_iter
peut donner n'importe lequel de T
, &T
ou &mut T
, selon le contexte.
- L'itérateur retourné par
iter
donnera &T
, par convention.
- L'itérateur retourné par
iter_mut
donnera &mut T
, par convention.
La première question est: "Qu'est-ce que c'est into_iter
?"
into_iter
vient 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 la manière dont un type particulier doit être converti en itérateur. Plus particulièrement, si un type implémente, IntoIterator
il peut être utilisé dans une for
boucle.
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 des 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 laissez pas berner par la signature into_iter(self)
car self
c'est une référence dans les deux cas) et leurs itérateurs produiront des références aux éléments à l'intérieur 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 donne 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. Leur type de retour est donc indépendant du contexte, et seront classiquement des itérateurs produisant respectivement des références immuables et des références mutables.
L'auteur de l'article Rust by Example illustre la surprise provenant 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:
IntoIterator
n'est pas implémenté pour [T; N]
, uniquement pour &[T; N]
et&mut [T; N]
- Lorsqu'une méthode n'est pas implémentée pour une valeur, elle est automatiquement recherchée pour les références à cette valeur à la place
ce qui est très surprenant into_iter
car 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 donne des valeurs car il ne peut pas "rétrécir" pour abandonner ses éléments.
Quant à savoir pourquoi les tableaux implémentent IntoIterator
(de manière si surprenante): c'est pour permettre d'itérer des références à eux dans des for
boucles.
into_iter
choisit une implémentation en fonction du fait que le récepteur est une valeur, une référence ou une référence mutable. (2) Il n'y a pas de valeurs mutables dans Rust, ou plutôt, toute valeur est mutable puisque vous en êtes propriétaire.&'a MyStruct
et&mut 'a MyStruct
et le premier était toujours choisi si présent même si j'ai appeléinto_iter().for_each()
sur lamut
valeur avec des&mut
arguments dans lambda.Je (un débutant de 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 sur les éléments par référenceinto_iter()
itère sur les éléments, les déplaçant dans la nouvelle portéeiter_mut()
itère sur les éléments, donnant une référence mutable à chaque élémentDonc,
for x in my_vec { ... }
c'est essentiellement équivalent àmy_vec.into_iter().for_each(|x| ... )
- les deuxmove
éléments demy_vec
dans la...
portée.Si vous avez juste besoin de "regarder" les données, utilisez
iter
, si vous avez besoin de les modifier / muter, utiliseziter_mut
, et si vous avez besoin de lui donner un nouveau propriétaire, utilisezinto_iter
.Cela a été utile: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Faire de ce wiki un wiki communautaire pour qu'un pro de Rust puisse, espérons-le, éditer cette réponse si j'ai commis des erreurs.
la source
iter
etinto_iter
..into_iter()
n'est pas implémenté pour un tableau lui-même, mais uniquement&[]
. Comparer:avec
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 queVec
lorsque vous utilisez les valeurs. (les valeurs ne peuvent pas être déplacées)Maintenant, pourquoi c'est le cas est un problème différent, et j'aimerais apprendre moi-même. Spéculer: le tableau est la donnée elle-même, la tranche n'est qu'une vue de celle-ci. En pratique, vous ne pouvez pas déplacer le tableau en tant que valeur dans une autre fonction, il vous suffit de passer une vue de celui-ci, vous ne pouvez donc pas non plus l'utiliser.
la source
IntoIterator
est également implémenté pour&'a mut [T]
, afin qu'il puisse déplacer les objets hors du tableau. Je pense que cela est lié au fait que la structure de retourIntoIter<T>
n'a pas d'argument à vie alors qu'elle leIter<'a, T>
fait, donc la première ne peut pas contenir de tranche.mut
signifie que vous pouvez modifier les valeurs, pas que vous pouvez les déplacer.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "erreur: impossible de sortir du contenu emprunté"ArrayIntoIter
structure en utilisant unsafe Rust, dans le cadre de la bibliothèque ... Peut-être que cela ne vaut pas la peine, comme vous devriezVec
de toute façon l' utiliser pour ces cas.array.into_iter
revient&T
- parce qu'il fait de la magie pour le convertir automatiquement en&array.into_iter
- et si c'est le cas, je ne comprends pas ce que cela a à voir avec le déplacement des valeurs ou le non déplacement des valeurs. Ou est-ce comme @rodrigo l'a dit, que vous obtenez la référence simplement parce que (pour une raison quelconque) vous ne pouvez pas déplacer des valeurs hors des tableaux ? Toujours très confus.Je pense qu'il y a quelque chose à clarifier un peu plus. Les types de collection, tels que
Vec<T>
etVecDeque<T>
, ont uneinto_iter
méthode qui produitT
parce qu'ils implémententIntoIterator<Item=T>
. Rien ne nous empêche de créer un typeFoo<T>
si celui-ci est itéré, il ne donnera pasT
mais un autre typeU
. Autrement dit, desFoo<T>
outilsIntoIterator<Item=U>
.En fait, il existe quelques exemples dans
std
: les&Path
outilsIntoIterator<Item=&OsStr>
et les&UnixListener
outilsIntoIterator<Item=Result<UnixStream>>
.La différence entre
into_iter
etiter
Revenons à la question initiale sur la différence entre
into_iter
etiter
. Comme d'autres l'ont souligné, la différence est queinto_iter
méthode requiseIntoIterator
peut donner n'importe quel type spécifié dansIntoIterator::Item
. En règle générale, si un type implémenteIntoIterator<Item=I>
, par convention, il a également deux méthodes ad hoc:iter
etiter_mut
qui donnent&I
et&mut I
, respectivement.Ce que cela implique, c'est que nous pouvons créer une fonction qui reçoit un type qui a une
into_iter
méthode (c'est-à-dire qu'il s'agit d'un itérable) en utilisant un trait lié:Cependant, nous ne pouvons pas * utiliser un trait lié pour exiger qu'un type ait une
iter
méthode ou uneiter_mut
méthode, car ce ne sont que des conventions. Nous pouvons dire queinto_iter
c'est plus largement utilisable queiter
ouiter_mut
.Alternatives à
iter
etiter_mut
Un autre intéressant à observer est que ce
iter
n'est pas la seule façon d'obtenir un itérateur qui cède&T
. Par convention (encore une fois), les types de collectionSomeCollection<T>
dansstd
lesquels ont laiter
méthode ont également leurs types de référence immuables&SomeCollection<T>
implémentésIntoIterator<Item=&T>
. Par exemple,&Vec<T>
implementsIntoIterator<Item=&T>
, donc cela nous permet d'itérer sur&Vec<T>
:Si cela
v.iter()
équivaut à&v
ces deux implémentationsIntoIterator<Item=&T>
, pourquoi alors Rust fournit-il les deux? C'est pour l'ergonomie. Dansfor
boucles, c'est un peu plus concis à utiliser&v
quev.iter()
; mais dans d'autres cas,v.iter()
c'est beaucoup plus clair que(&v).into_iter()
:De même, dans
for
boucles,v.iter_mut()
peut être remplacé par&mut v
:Quand fournir (implémenter)
into_iter
etiter
méthodes pour un typeSi le type n'a qu'une seule «manière» d'être itérée, nous devons implémenter les deux. Cependant, s'il y a deux façons ou plus de l'itérer, nous devrions plutôt fournir une méthode ad hoc pour chaque façon.
Par exemple,
String
ne fournit niinto_iter
niiter
car il existe deux façons de l'itérer: pour itérer sa représentation en octets ou pour itérer sa représentation en caractères. Au lieu de cela, il fournit deux méthodes:bytes
pour itérer les octets etchars
pour itérer les caractères, comme alternatives à laiter
méthode.* Eh bien, techniquement, nous pouvons le faire en créant un trait. Mais ensuite, nous avons besoin de
impl
ce trait pour chaque type que nous voulons utiliser. Pendant ce temps, de nombreux types sontstd
déjà mis en œuvreIntoIterator
.la source