Contexte de la question :
La construction for-range est apparue en Go comme un moyen d'itérer sur des collections (slices, tableaux, maps, chaînes). Les développeurs de Go ont introduit une optimisation : à chaque itération de la boucle, une copie de la valeur est effectuée au lieu d'utiliser directement la référence, ce qui peut conduire à des erreurs non évidentes, en particulier avec les variables de boucle.
Problème :
Beaucoup d'utilisateurs se trompent en essayant de prendre l'adresse d'une variable à l'intérieur du range (par exemple, &v), croyant qu'ils obtiennent l'adresse d'un élément de la collection, alors qu'en réalité, ils obtiennent l'adresse d'une variable locale.
Solution :
Dans la boucle for-range, à chaque itération, de nouvelles copies des variables d'itération (key, value) sont créées. Pour les types simples, cela ne pose pas problème, mais pour les structures, cela entraîne des surprises lors de la conservation d'un pointeur sur l'élément — il pointera toujours sur la même variable, et non sur des éléments différents du slice.
Exemple de code :
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // tous les ptrs pointeront sur le même p }
Caractéristiques clés :
Que se passera-t-il lors de la conservation des références à la variable value à l'intérieur du range ?
Toutes les références pointeront vers la même mémoire, car value est une variable temporaire.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // Tous les ptrs contiennent une référence vers la même variable !
Peut-on modifier un élément de la collection par référence via value dans range ?
Non, modifier value ne touche pas l'élément original de la collection. Pour modifier, il est nécessaire d'accéder par index.
for _, v := range arr { v.Field = 10 // arr ne sera pas modifié } for i := range arr { arr[i].Field = 10 // correct }
for-range sur map garantit-il un ordre d'itération séquentiel ?
Non, l'ordre d'itération sur map en Go n'est pas défini et peut être différent à chaque exécution de l'application.
Un développeur essaie de sérialiser une liste de références aux éléments d'une structure via range et conserve &value dans un slice séparé. Le résultat est un slice avec des adresses identiques.
Avantages :
Inconvénients :
Ils itèrent par index et conservent le pointeur vers l'élément souhaité du tableau :
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Avantages :
Inconvénients :