Maison  >  Article  >  développement back-end  >  À quel point la boucle for dans le langage Go est-elle pitoyable ?

À quel point la boucle for dans le langage Go est-elle pitoyable ?

藏色散人
藏色散人avant
2022-11-07 16:48:132565parcourir

Cet article est écrit par la rubrique tutoriel golang pour vous présenter les questions d'entretien sur la boucle Go for. Je ne sais pas ce que vous savez sur la boucle for. Pensez-vous que c'est un piège ? Ci-dessous, je parlerai en détail des problèmes liés à cela. J'espère que cela sera utile aux amis qui en ont besoin !

Je ne sais pas combien de questions d'entretien Go et de fuites sont liées aux boucles for. Aujourd'hui, j'ai regardé de plus près ce week-end et découvert la redéfinition de la sémantique des variables de boucle.

Le célèbre patron du hardcore Russ Cox a déclaré qu'il avait étudié cette question et a déclaré que dix années d'expérience montrent que le coût de la sémantique actuelle est très élevé.

Problème

  • Cas 1 : Récupération des caractères d'adresse

Dans le langage Go, lorsque nous écrivons une instruction for, parfois les résultats de l'opération et de la supposition sont incohérents. Par exemple, le code du premier cas ci-dessous :

var all []*Itemfor _, item := range items {
	all = append(all, &item)
}

Y a-t-il un problème avec ce code ? Qu'est-ce qui est stocké dans la variable item dans la variable all ? Est-ce la valeur de l'élément de chaque boucle ?

En fait, lors de la boucle for, le même élément est stocké à chaque fois dans la variable, qui est la valeur de l'élément de la dernière boucle.

C'est une question qui apparaît souvent dans les interviews Go. Elle est plus intéressante lorsqu'elle est combinée avec goroutine. Après tout, il y a encore des problèmes comme une sortie dans le désordre.

Si vous souhaitez résoudre ce problème, vous devez réécrire le programme comme suit :

var all []*Itemfor _, item := range items {
	item := item
	all = append(all, &item)
}

Re-déclarez une variable d'élément, stockez la variable d'élément de la boucle for puis ajoutez-la.

  • Cas 2 : Fonction de fermeture

Voici le code du deuxième cas :

var prints []func()for _, v := range []int{1, 2, 3} {
	prints = append(prints, func() { fmt.Println(v) })
}for _, print := range prints {	print()
}

Quel est le résultat de ce programme ? Sans & pour obtenir le caractère d'adresse, affiche-t-il 1, 2, 3 ?

Le résultat de sortie est 3, 3, 3. Pourquoi est-ce ?

L'un des points clés du problème est la fonction de fermeture. En fait, toutes les fermetures impriment le même v. La sortie est 3 car après la fin de la boucle for, la valeur finale de v est définie sur 3, et c'est tout.

Si vous souhaitez obtenir l'effet souhaité, vous devez toujours utiliser la réaffectation universelle. Le code réécrit est le suivant :

for _, v := range []int{1, 2, 3} {
		v := v
		prints = append(prints, func() { fmt.Println(v) })
	}

Ajoutez l'instruction v := v et les résultats de sortie du programme sont 1, 2, 3. Parcourez attentivement les projets Go que vous avez écrits. Vous sont-ils tous familiers ? Avec cette méthode de transformation, nous avons gagné. v := v 语句,程序输出结果为 1,2,3。 仔细翻翻你写过的 Go 工程,是不是都很熟悉?就这改造方法,赢了。

尤其是配合上 Goroutine 的写法,很多同学会更容易在此翻车。

解决方案

  • 修复思路

实际上 Go 核心团队在内部和社区已经讨论过许久,希望重新定义 for 循环的语法。要达到的目的是:使循环变量每次迭代而不是每次循环

解决的办法是:在每个迭代变量 x 的每个循环体开头,加一个隐式的再赋值,也就是 x := x

Surtout avec la méthode d'écriture Goroutine, de nombreux élèves auront plus de chance de se renverser ici.
  • Solution

Idées de réparation

En fait, l'équipe principale de Go discute depuis longtemps en interne et dans la communauté, dans l'espoir de redéfinir la syntaxe des boucles for. Ce que vous voulez réaliser est :

Faites en sorte que la variable de boucle soit à chaque itération au lieu de chaque fois dans la boucle

.

La solution est : ajouter une réaffectation implicite

au début de chaque corps de boucle de chaque variable d'itération x, c'est-à-dire x := x, qui peut résoudre le programme ci-dessus La fosse cachée. C'est la même chose que ce que nous faisons maintenant, sauf que nous l'ajoutons manuellement. L'équipe Go le gère implicitement dans le compilateur.

Laissez les utilisateurs décider par eux-mêmes

Ce qui est plus embarrassant, c'est que l'équipe Go interdit de redéfinir le langage dans Proposition : transition Go 2, donc rsc ne peut pas le faire directement.

Ce sera donc à l'utilisateur de contrôler ce "break" en changeant la sémantique en fonction de la ligne go dans le fichier go.mod de chaque package.

Si nous modifions la boucle for discutée dans cet article en itération dans Go1.30, alors la déclaration de version go dans le fichier go.mod sera une clé.

Comme indiqué ci-dessous :

🎜🎜🎜Go 1.30 ou supérieur itérera la variable à chaque fois, tandis que les versions antérieures de Go boucleront la variable à chaque fois. 🎜🎜De cette façon, les problèmes de boucle for mentionnés ci-dessus seront résolus dans une certaine mesure. 🎜🎜🎜Résumé🎜🎜🎜Le problème des variables dans les boucles for a toujours été un sujet favori des principaux examinateurs de Go. De plus, il est vrai que de tels pièges seront rencontrés lors de la programmation du code Go. 🎜🎜Bien que rsc espère être pionnier dans les fichiers go.mod et utiliser les déclarations de version go pour modifier la sémantique (l'ajout et la suppression ne sont pas autorisés). Cela ouvre sans aucun doute une porte dérobée à la garantie de compatibilité Go1. 🎜🎜S'il est mis en œuvre, ce changement entraînera une sémantique différente entre les versions précédentes et ultérieures de Go. Autant devenir un commutateur sémantique dans un fichier go.mod. 🎜🎜C'est évidemment une question très difficile à laquelle réfléchir. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer