Sortie de Go 1.22
(go.dev)- La variable de boucle
forest désormais recréée à chaque itération, ce qui évite les bugs de partage involontaire, etrangepeut aussi s’appliquer aux entiers - La compatibilité respecte toujours la promesse de compatibilité Go 1, et l’on s’attend à ce que presque tous les programmes Go se compilent et s’exécutent comme auparavant
- Dans un workspace, il est possible d’utiliser le répertoire vendor généré avec
go work vendor, et si unvendorde workspace est présent,-mod=vendordevient la valeur par défaut - Fin de la prise en charge de
go gethors module dans le mode legacyGOPATHavecGO111MODULE=off, tandis que les autres commandes de build commego buildetgo testcontinuent de fonctionner pour les programmes legacyGOPATH - Le runtime place désormais les métadonnées GC basées sur les types à proximité des objets du tas, ce qui améliore les performances CPU des programmes Go de 1 à 3 % et réduit d’environ 1 % la surcharge mémoire de nombreux programmes
- L’alignement d’adresse de certains objets peut passer de 16 octets ou plus à un alignement sur 8 octets, et le code assembleur supposant un alignement supérieur doit être modifié ; la solution de contournement temporaire
GOEXPERIMENT=noallocheaderspeut être utilisée - Les builds avec PGO peuvent dévirtualiser davantage d’appels, et sur un ensemble représentatif de programmes Go, l’activation de PGO améliore les performances à l’exécution de 2 à 14 %
- Ajout à la bibliothèque standard du premier package
v2,math/rand/v2, sans héritage deRead, avec un générateur global toujours initialisé aléatoirement, les sourcesChaCha8etPCG, ainsi que la fonction génériqueN - Les motifs de routage de
net/http.ServeMuxprennent désormais en charge les méthodes et les jokers, avec de petites ruptures de compatibilité dans les motifs contenant{}et dans le traitement des chemins échappés ; il est possible de restaurer l’ancien comportement avecGODEBUG=httpmuxgo121=1 - Dans
crypto/tls, la version minimale par défaut côté serveur passe à TLS 1.2 lorsqu’elle n’est pas spécifiée, et les suites de chiffrement sans ECDHE ne sont plus proposées par défaut lors des handshakes antérieurs à TLS 1.3 - Le traceur d’exécution a été entièrement remanié : il utilise l’horloge de l’OS sur la plupart des plateformes, prend en charge des traces segmentées compatibles avec un traitement en streaming, et fournit la durée complète des syscalls ainsi que des informations sur le thread OS ayant exécuté une goroutine
- L’interface web de l’outil
tracea été mise à jour dans le cadre du nouveau support du traceur, et prend en charge, pour les traces de programmes compilés avec Go 1.22 ou plus, une vue orientée thread ainsi que l’affichage de la durée complète des syscalls vetajuste ses avertissements sur les fermetures de boucle pour refléter la sémantique des variables de boucle de Go 1.22, et ajoute des avertissements pourappendsans valeur,time.Sincenon différé dansdefer, ainsi que pour les incohérences clé/valeur danslog/slog- Le bootstrap nécessite Go 1.20 au moins dans son dernier point release, et il est prévu que Go 1.24 exige au minimum le dernier point release de Go 1.22
- Sur
darwin/amd64, la toolchain Go génère désormais des PIE par défaut, et Go 1.22 est la dernière version à fonctionner sur macOS 10.15 Catalina ; Go 1.23 exigera macOS 11 Big Sur ou plus récent - Le type
Aliaset la fonctionUnaliasont été ajoutés àgo/types, et avecgotypesalias=1, les clients doivent prendre en charge le typeAlias slices.Delete,DeleteFunc,Compact,CompactFuncetReplaceremettent à zéro les éléments de la plage réduite, etInsertdéclenche désormais toujours un panic siiest hors limites- Les fonctions de lecture de répertoires sous Windows lisent désormais les entrées par lots pour réduire le nombre d’appels système, avec des gains de performances pouvant atteindre 30 %
1 commentaires
Avis sur Hacker News
Ces trois dernières années, j’ai surtout utilisé TypeScript, puis je me suis récemment mis à Go. Au début, j’étais un peu inquiet à cause du manque de fonctions sur les tableaux et d’un système de types moins flexible.
Mais en reconfigurant un projet TypeScript pour une équipe de 4 à 5 personnes, je me suis rendu compte qu’on passait énormément de temps sur le linting, le choix d’une bibliothèque de routage serveur, le choix du serveur, les standards de code, la gestion d’erreurs de base, l’imposition d’erreurs personnalisées ou d’un type
Resultpour éviter l’enfer destry/catchimbriqués, les tests et les mocks, la configuration de Prisma, etc.J’aime TypeScript, mais utiliser un langage avec une excellente bibliothèque standard, une vraie sûreté de typage et des standards de code intégrés est vraiment confortable. Go a aussi ses quirks, mais il est plutôt correct, et le fait que même le routage arrive dans la bibliothèque standard enlève encore un point auquel penser, ce qui est appréciable. Maintenant, j’attends juste les fonctions
map/filter/findpour les slices.À chaque release, TypeScript devient de plus en plus complexe pour les power users, tandis que Go ajoute des éléments qui manquaient, comme
rangesur les entiers, afin de rendre son utilisation plus simple.C’est un peu comme les suites de jeux qui ajoutent sans cesse des paramètres et des mécaniques, jusqu’à avoir besoin d’un reset pour les nouveaux joueurs. Comme c’est difficile à faire pour un langage de programmation, c’est bien que Go en soit conscient.
go:embedetgo:generatem’impressionne chaque fois que je le vois dans d’autres projets.Les paquets
golang.org/xsont aussi appréciables, avec presque toutes les implémentations liées aux RFC Internet prêtes à l’emploi.map/filter/findpour les slices, on peut utiliser la bibliothèque lo, inspirée de lodash.C’est mon couteau suisse favori dans les projets Go : https://github.com/samber/lo
À chaque fois que je lisais la doc pour faire quelque chose, quelqu’un finissait vite par m’indiquer un truc utilisable qui n’était pas dans la documentation.
Si les notes de version officielles vous semblent un peu austères, j’en ai fait une version interactive : https://antonz.org/go-1-22
forne m’a pas paru évident au départ, mais voir le résultat inattendu en l’exécutant moi-même m’a aidé.CompactetReplaceprêtent à confusion ; je ne sais pas si c’est bien correct.J’utilise Go depuis plus de 9 ans, et ces 4 dernières années j’ai beaucoup utilisé Dart à cause de Flutter. À mon avis, les deux langages ont des attitudes diamétralement opposées face à la complexité. Dart essaie d’ajouter toutes les fonctionnalités possibles, Go fait l’inverse.
Premièrement, dans Dart, il y a beaucoup trop de façons d’initialiser des valeurs dans une classe, ce qui fait perdre beaucoup de temps. Selon les préfixes
final/const/static/late, l’endroit d’initialisation change : constructeur, factory,initState()d’unStatefulWidgetFlutter, etc. Après un refactoring, il faut souvent retravailler largement l’initialisation. Les getters qui ressemblent à des variables ajoutent aussi de la confusion dans une nouvelle codebase.Deuxièmement, Dart offre beaucoup de fonctionnalités faciles à condenser en une ligne — maps, streams, etc. — ce qui donne envie de produire rapidement du code en une seule ligne difficile à comprendre. Mais au débogage, ou quand un développeur junior tombe sur des hacks comme
firstWhereOrNullet des paramètres optionnels commeorElse: () => null, c’est très déroutant. La null safety obscure ou les erreurs de type bloquent aussi souvent, et les remplacer par une simple boucleforà la Go procure un vrai soulagement.initStatepour ce qui n’est pas directement lié à l’UI. Par exemple, je l’éviterais sauf pour quelque chose comme définir l’indication d’un champ texte.Sur la plupart des pages Flutter, soit il y a très peu à faire, soit le parent crée un objet
MyPageController, l’initialise comme il le souhaite, puis la page enfant se comporte en fonction de ce contrôleur. Par exemple, dans une page de liste, quand l’utilisateur veut modifier un élément, on crée un contrôleur, on y place l’élément et on l’envoie à la page enfant ; au retour, le parent peut regarder l’élément du contrôleur, ses variables ou ses callbacks pour décider des changements d’UI.Cela permet de contrôler finement les interactions entre widgets sans toucher à des choses comme
InheritedWidget. Bien sûr, c’est mieux de l’utiliser avec une bibliothèque de gestion d’état, mais en pratique on s’en passe souvent.range, c’est pareil : au final, le nombre de caractères tapés est similaire à une compréhension du typeforEach(f), mais comme c’est réparti sur trois lignes, ça paraît simplement plus long ; quand on l’écrit explicitement, il n’y a rien de spécial.En revanche, pour l’initialisation, j’aimerais qu’il existe en Go une manière idiomatique d’initialiser les champs d’une structure à des valeurs spécifiques. L’absence de constructeur ne me dérange pas, mais il m’arrive de vouloir qu’un
boolsoit initialisé àtruepar défaut.initStateest une surcharge deStatefulWidget, ne diffère pas des événements de cycle de vie d’autres frameworks, et relève d’un concept Flutter ; ce n’est donc pas quelque chose qu’on confond naturellement avec un constructeur.On peut hésiter entre un constructeur normal et une factory, mais il est difficile de dire que Go est supérieur à cause de
initState.Quand
io.Copycopie depuisTCPConnversUnixConn, il utilise si possible l’appel système Linuxsplice(2), et passe par la nouvelle méthodeTCPConn.WriteTo.Grâce à une mise à niveau d’interface, cela augmente encore une fois de manière transparente les E/S sans copie. L’impact que le package
ioobtient avec ce pattern est vraiment énorme.Cela dit, ça reste dans les limites de ce à quoi on peut s’attendre.
Les modèles de routage améliorés sont une bonne évolution, maintenant que l’avenir de Gorilla Mux est incertain
Cela dit, ce changement casse la rétrocompatibilité de façon limitée : certains points sont évidents, comme le comportement différent des modèles contenant
{et}, d’autres le sont moins, comme le traitement des chemins échappés. C’est contrôlé par le champhttpmuxgo121deGODEBUG, et définirhttpmuxgo121=1permet de revenir à l’ancien comportementJe me demande toutefois pourquoi cela n’entre pas en conflit avec la promesse de compatibilité Go 1, selon laquelle « les programmes écrits pour la spécification Go 1 continueront à compiler et à s’exécuter correctement, sans modification, pendant toute la durée de vie de cette spécification »
godansgo.modEn 1.22 ou plus, on obtient le nouveau comportement de la bibliothèque ; sinon, l’ancien. Reste à voir à quel point cela fonctionnera bien en pratique
{et}sont utilisés dans les chemins des handlers HTTP étaient très raresIls fournissent aussi un moyen de désactiver le nouveau comportement, donc ils ne forcent pas à modifier le code, même s’il faut définir une nouvelle variable d’environnement
Le changement de sémantique des boucles
fordans cette version est un exemple similaire : c’est, de fait, un changement cassant. Mais tous les programmes Go continuent de compiler et de s’exécuter ; certains comportements changent juste un peu. Go a choisi une approche pragmatique, et je pense que c’est l’une des raisons de son succèsgo.modpour casser les choses selon la version, ils devraient au moins les casser d’une meilleure manièreQuand on passe
go.modà 1.22, je préférerais largement que le code échoue à la compilation plutôt que de devoir traquer des problèmes subtils à l’exécutionDans Go 1.22, chaque itération d’une boucle crée désormais une nouvelle variable
Cela avait déjà été discuté ici : https://news.ycombinator.com/item?id=33160236 - Go: Redefining For Loop Variable Semantics (2022)
C’est peut-être vieux jeu, mais je n’aime pas l’ajout de
rangesur les fonctions. Le gain de confort ne me semble pas suffisant pour justifier la complexité ajoutée au langage, et le style fonctionnel paraît en décalage avec le style explicite, impératif, verbeux mais peu chargé en fonctionnalités de Go. Je pense que c’était l’un des grands atouts de GoPour la même raison, je pense que
rangesur les entiers va dans la mauvaise direction. L’ensemble de fonctionnalités réduit de Go et sa faible charge cognitive ont toujours été des forces majeures et un élément différenciantL’un des grands problèmes du développement logiciel est d’empêcher les développeurs d’ajouter de la complexité ou de vouloir montrer leur talent. Les développeurs en début de carrière, en particulier, ont tendance à surcomplexifier les solutions plutôt que de résoudre le problème de façon directe, moins élégante et plus verbeuse, puis de passer à autre chose
La prise en charge actuelle des génériques dans Go n’est pas assez sophistiquée pour la soutenir, ce qui impose des limitations étrangement artificielles
L’ajout de
sql.Null[T]est une bonne chose. Je l’utiliserai probablement dans les nouveaux projetsDans mon travail actuel, je dépends de null de sqlboiler [0], dont l’API est très similaire. Il se comporte comme
sql.Null, mais ajoute une méthodeIsSet() boolqui indique si la valeur a déjà été explicitement définie, ce qui aide à distinguer un « null intentionnel » d’un « null parce que non initialisé ». Ça a l’air bien, mais en pratique je ne l’ai jamais utiliséJ’ai hâte de réexaminer le mux de la bibliothèque standard pour voir s’il serait aussi possible de supprimer chi. C’est vraiment appréciable de voir ce genre de fonctionnalité arriver dans la bibliothèque standard
Quand ce genre de fonctionnalité entre dans la bibliothèque standard, on a la garantie qu’elle sera toujours maintenue et qu’une même approche sera utilisée dans beaucoup de programmes Go
Je me demande, quand on utilise Go en production, si l’on peut passer rapidement aux dernières versions ou si l’on reste coincé sur d’anciennes releases
En regardant des projets publics, j’ai l’impression qu’ils évitent souvent les fonctionnalités relativement récentes. Même sans utiliser les génériques, j’aimerais utiliser
any, apparu avec Go 1.18, plutôt que l’ancieninterface{}, et j’ai aussi entendu dire que le premier était plus « idiomatique »Cette fois, les choses se sont un peu compliquées : l’équipe Go a sorti la 1.22 avant que nous ayons passé la version par défaut à la 1.21. À ce stade, attendre relève presque de la convention, et il est rare qu’une release de Go casse quelque chose
Par exemple, pour les génériques, nous avons attendu environ six mois avant de les utiliser réellement, afin de vérifier leur utilité, l’expérience développeur et l’absence d’impact important sur les temps de build et de test
Mais nous fournissons aussi des applications utilisateur final en Go, et celles-ci doivent rester en 1.20 pour des raisons de compatibilité avec d’anciens OS clients. Comme il y a eu une coupure assez importante en 1.21, il est probable que nous restions en 1.20 pendant quelques années
latestde Go, et comme la suite de tests est assez solide, les mises à jour se font en pratique automatiquement. Si quelque chose ne va pas, le pipeline casse, et nous décidons alors soit d’épingler la dernière version fonctionnelle, soit de retarder la mise à jour jusqu’à corriger le changement cassantTous les quelques années, une nouvelle version révèle parfois un bug subtil de cas limite entre Go et un package tiers, mais par définition ce type de bug a échappé à la suite de tests ; il est donc probable qu’une mise à niveau manuelle ne l’aurait pas détecté non plus
Si la prise en charge d’anciens compilateurs Go vous inquiète, vous pouvez ajouter des conditions de build pour les anciennes versions et définir ce qui manque. Par exemple, sous
// +build !go1.7, on peut mettretype any = interface{}