Go 1.22 veröffentlicht
(go.dev)for-Schleifenvariablen werden nun bei jeder Iteration neu erzeugt, um unbeabsichtigte Sharing-Bugs zu vermeiden; außerdem istrangeüber Ganzzahlen möglich- Die Kompatibilität hält das Go-1-Kompatibilitätsversprechen ein; es wird erwartet, dass nahezu alle Go-Programme wie bisher kompilieren und laufen
- In Workspaces kann ein mit
go work vendorerzeugtes vendor-Verzeichnis verwendet werden; wenn ein Workspace-vendorvorhanden ist, wird-mod=vendorzum Standardwert - Unterstützung für
go getaußerhalb von Modulen im Legacy-GOPATH-Modus mitGO111MODULE=offeingestellt; andere Build-Befehle wiego buildundgo testfunktionieren weiterhin für Legacy-GOPATH-Programme - Die Runtime legt typbasierte GC-Metadaten nun näher an Heap-Objekten ab, wodurch sich die CPU-Performance von Go-Programmen um 1–3 % verbessert und der Speicher-Overhead vieler Programme um etwa 1 % sinkt
- Die Adressausrichtung einiger Objekte kann sich von bisher 16 Byte oder mehr auf 8 Byte ändern; Assembly-Code, der eine höhere Ausrichtung voraussetzt, muss angepasst werden; als temporärer Workaround kann
GOEXPERIMENT=noallocheadersverwendet werden - PGO-Builds können mehr Aufrufe devirtualisieren; bei einer repräsentativen Sammlung von Go-Programmen verbessert sich die Runtime-Performance mit aktiviertem PGO um 2–14 %
- Mit
math/rand/v2wurde das erstev2-Paket in die Standardbibliothek aufgenommen; es übernimmtReadnicht, bietet einen globalen Generator mit immer zufälligem Seed,ChaCha8- undPCG-Quellen sowie die generische FunktionN - Routing-Patterns von
net/http.ServeMuxunterstützen Methoden und Wildcards; bei Patterns mit{}und der Verarbeitung escapeter Pfade gibt es kleine rückwärtsinkompatible Änderungen, das frühere Verhalten kann mitGODEBUG=httpmuxgo121=1wiederhergestellt werden - Die standardmäßige Mindestversion für
crypto/tls-Server wird, sofern nicht angegeben, auf TLS 1.2 geändert; bei Pre-TLS-1.3-Handshakes werden Cipher Suites ohne ECDHE standardmäßig nicht mehr angeboten - Der Execution Tracer wurde grundlegend überarbeitet: Auf den meisten Plattformen nutzt er die OS-Uhr und bietet in Segmente aufgeteilte, streambare Traces, die vollständige Dauer von Syscalls sowie Informationen zum OS-Thread, auf dem eine Goroutine ausgeführt wurde
- Die Web-UI des
trace-Tools wurde im Rahmen der Unterstützung für den neuen Tracer aktualisiert und unterstützt bei Traces von Programmen, die mit Go 1.22 oder neuer gebaut wurden, eine threadorientierte Ansicht sowie die Anzeige der vollständigen Syscall-Dauer vetpasst die Loop-Closure-Warnungen an die neue Bedeutung von Schleifenvariablen in Go 1.22 an und ergänzt Warnungen fürappendohne genutzten Wert, nicht verzögertestime.Sinceindefersowie nicht übereinstimmende Key/Value-Paare inlog/slog- Für das Bootstrap ist mindestens das letzte Point Release von Go 1.20 erforderlich; Go 1.24 wird voraussichtlich mindestens das letzte Point Release von Go 1.22 verlangen
- Unter
darwin/amd64erzeugt die Go-Toolchain standardmäßig PIE; Go 1.22 ist das letzte Release, das unter macOS 10.15 Catalina läuft, und Go 1.23 wird macOS 11 Big Sur oder neuer erfordern - In
go/typeswurden der TypAliasund die FunktionUnaliashinzugefügt; beigotypesalias=1müssen Clients den TypAliasverarbeiten slices.Delete,DeleteFunc,Compact,CompactFuncundReplacesetzen Elemente im verkleinerten Bereich auf zero;Insertlöst immer eine Panic aus, wenniaußerhalb des gültigen Bereichs liegt- Windows-Funktionen zum Lesen von Verzeichnissen lesen Einträge stapelweise, um die Zahl der Systemaufrufe zu reduzieren; die Performance verbessert sich um bis zu 30 %
1 Kommentare
Hacker-News-Meinungen
In den letzten drei Jahren habe ich hauptsächlich TypeScript verwendet und erst vor Kurzem mit Go angefangen. Anfangs war ich wegen fehlender Array-Funktionen und des weniger flexiblen Typsystems etwas besorgt.
Als ich dann aber wieder ein TypeScript-Projekt für ein Team von 4–5 Personen aufgesetzt habe, ging extrem viel Zeit für Linting, die Wahl einer Server-Routing-Library, die Server-Auswahl, Coding Standards, grundlegendes Error Handling, eigene Fehler oder erzwungene
Result-Typen, um die Hölle verschachteltertry/catch-Blöcke zu vermeiden, Tests und Mocking, Prisma-Konfiguration usw. drauf.Ich mag TypeScript zwar, aber es ist wirklich angenehm, eine Sprache mit hervorragender Standardbibliothek, echter Typsicherheit und eingebauten Coding Standards zu nutzen. Go hat auch seine Eigenheiten, ist aber ziemlich gut, und dass jetzt sogar Routing in die Standardbibliothek aufgenommen wurde, nimmt einem eine weitere Sache ab, um die man sich kümmern muss. Jetzt warte ich nur noch auf
map/filter/find-Funktionen für Slices.TypeScript wird mit jedem Release für Power User immer komplexer, während Go fehlende Dinge wie
rangeüber Integer ergänzt und dadurch die Nutzung einfacher macht.Es ist ein bisschen wie bei Spiel-Fortsetzungen, die immer mehr Settings und Mechaniken anhäufen, bis für neue Nutzer ein Reset nötig wird. Bei Programmiersprachen ist das schwer, daher ist es gut, dass Go sich dessen bewusst ist.
go:embedundgo:generatemachen kann, beeindruckt mich jedes Mal, wenn ich es in anderen Projekten sehe.Auch die
golang.org/x-Pakete sind großartig, weil Implementierungen zu praktisch allen internetbezogenen RFCs direkt einsatzbereit sind.map/filter/findfür Slices kommen, kann man die von lodash inspirierte lo-Library verwenden.In Go-Projekten ist sie mein gern genutztes Schweizer Taschenmesser: https://github.com/samber/lo
Immer wenn man etwas tun wollte und die Dokumentation las, kam kurz darauf jemand und zeigte einem einen brauchbaren Trick, der nicht in der Doku stand.
Wenn die offiziellen Release Notes etwas trocken wirken: Ich habe eine interaktive Version erstellt: https://antonz.org/go-1-22
for-Schleifen habe ich anfangs nicht sofort verstanden; es hat geholfen, es selbst auszuführen und das unerwartete Ergebnis zu sehen.CompactundReplacesind verwirrend; ich bin mir nicht sicher, ob das so stimmt.Ich nutze Go seit über neun Jahren und habe in den letzten vier Jahren wegen Flutter viel Dart verwendet. Meiner Ansicht nach haben die beiden Sprachen eine genau gegensätzliche Haltung zu Komplexität. Dart versucht, jedes mögliche Feature hinzuzufügen, Go tut das Gegenteil.
Erstens gibt es in Dart viel zu viele Möglichkeiten, Werte innerhalb einer Klasse zu initialisieren, was viel Zeit kostet. Je nach Präfix
final/const/static/lateunterscheiden sich Konstruktoren, Factories,initState()von Flutter-StatefulWidgetund andere Initialisierungsorte; beim Refactoring muss man die Initialisierung oft stark überarbeiten. Getter, bei denen Funktionen wie Variablen aussehen, sorgen in neuen Codebases ebenfalls für zusätzliche Verwirrung.Zweitens hat Dart viele Features, mit denen sich Maps, Streams usw. gut in eine einzige Zeile quetschen lassen, wodurch man schnell versucht ist, schwer verständlichen Einzeiler-Code zu schreiben. Beim Debuggen oder wenn Junior-Entwickler auf Hacks wie
firstWhereOrNullund optionale Parameter wieorElse: () => nullstoßen, sind sie jedoch sehr verwirrt. Auch undurchsichtige Null-Safety- oder Typfehler halten einen oft auf, und es ist eine echte Erleichterung, das in eine einfache Go-artigefor-Schleife umzuschreiben.initStatebesser nicht verwenden. Ich vermeide es, außer es geht beispielsweise nur darum, einen Hint für ein Textfeld zu setzen.Die meisten Flutter-Seiten haben entweder sehr wenig zu tun, oder der Parent erstellt ein
MyPageController-Objekt, initialisiert es wie gewünscht und lässt die Child-Seite anhand dieses Controllers arbeiten. Wenn ein Nutzer etwa auf einer Listenseite ein Element bearbeiten will, erstellt man einen Controller, legt das Element hinein und übergibt ihn an die Child-Seite; nach der Rückkehr kann der Parent anhand des Elements im Controller oder anhand von Variablen und Callbacks entscheiden, wie sich die UI ändern soll.So lässt sich die Interaktion zwischen Widgets fein steuern, ohne Dinge wie
InheritedWidgetanzufassen. Natürlich ist es gut, das zusammen mit einer State-Management-Library zu nutzen, aber in der Praxis tut man das oft auch nicht.rangeist es ähnlich: Am Ende tippt man ungefähr so viele Zeichen wie bei einer Comprehension à laforEach(f), nur wirkt es länger, weil es auf drei Zeilen verteilt ist; wenn man es ausschreibt, ist es keine große Sache.Was Initialisierung angeht, würde ich mir allerdings eine idiomatische Möglichkeit wünschen, Struct-Felder in Go auf bestimmte Werte zu initialisieren. Dass es keine Konstruktoren gibt, ist in Ordnung, aber manchmal möchte man, dass ein
boolstandardmäßig auftrueinitialisiert wird.initStateist ein Override vonStatefulWidgetund unterscheidet sich nicht von Lifecycle-Events in anderen Frameworks; es ist ein Flutter-Konzept und sollte nicht mit Konstruktoren verwechselt werden.Man kann sich zwar fragen, ob man einen normalen Konstruktor oder eine Factory verwenden sollte, aber wegen
initStatelässt sich kaum behaupten, Go sei überlegen.Wenn
io.CopyvonTCPConnnachUnixConnkopiert, verwendet es, wenn möglich, den Linux-Systemaufrufsplice(2)und nutzt die neue MethodeTCPConn.WriteTo.Durch ein Interface-Upgrade wurde also erneut transparent mehr Zero-Copy-I/O ermöglicht. Der Effekt, den das
io-Paket mit diesem Muster erzielt, ist wirklich groß.Trotzdem liegt es im erwartbaren Rahmen.
Verbesserte Routing-Patterns sind eine gute Verbesserung, gerade jetzt, wo die Zukunft von Gorilla Mux unsicher ist.
Allerdings bricht diese Änderung in kleinem Umfang die Abwärtskompatibilität: Es gibt offensichtliche Stellen, etwa dass sich das Verhalten von Patterns mit
{und}ändert, und weniger offensichtliche, etwa die Behandlung escapeter Pfade. Gesteuert wird das über das Feldhttpmuxgo121inGODEBUG; mithttpmuxgo121=1lässt sich das frühere Verhalten wiederherstellen.Ich frage mich aber, warum das nicht mit dem Go-1-Kompatibilitätsversprechen kollidiert: „Programme, die nach der Go-1-Spezifikation geschrieben wurden, werden während der Lebensdauer dieser Spezifikation weiterhin unverändert kompilieren und korrekt laufen.“
go-Direktive ingo.modzu koppeln.Ab 1.22 bekommt man das neue Library-Verhalten, andernfalls das alte. Wie gut das in der Praxis funktioniert, wird sich zeigen.
{und}in HTTP-Handler-Pfaden kaum genutzt werden.Außerdem gibt es eine Möglichkeit, das neue Verhalten abzuschalten, sodass keine Codeänderung erzwungen wird; man muss allerdings eine neue Umgebungsvariable setzen.
Die Änderung der Semantik von
for-Schleifen in diesem Release ist ein ähnliches Beispiel, im Grunde ebenfalls eine Breaking Change. Trotzdem kompilieren und laufen alle Go-Programme weiter, nur manches Verhalten ändert sich leicht. Go hat einen pragmatischen Ansatz gewählt, und ich denke, das war einer der Gründe für seinen Erfolg.go.modnutzt, um versionsabhängig etwas zu brechen, sollte man es wenigstens auf bessere Weise brechen.Wenn man
go.modauf 1.22 ändert, wäre es viel besser, der Code würde nicht kompilieren, statt subtile Laufzeitprobleme aufspüren zu müssen.In Go 1.22 erzeugt jede Iteration einer Schleife eine neue Variable.
Das wurde hier schon früher diskutiert: https://news.ycombinator.com/item?id=33160236 – Go: Redefining For Loop Variable Semantics (2022)
Vielleicht bin ich altmodisch, aber die Ergänzung von
rangeüber Funktionen gefällt mir nicht. Der Komfort ist nicht groß genug, um die zusätzliche Komplexität in der Sprache zu rechtfertigen, und der funktionale Stil scheint nicht zu Gos explizitem, imperativem und wortreichem, aber funktionsarmem Stil zu passen. Das war meiner Meinung nach eine der großen Stärken von Go.Aus demselben Grund halte ich auch
rangeüber Integers für die falsche Richtung. Gos schlanker Funktionsumfang und die geringe kognitive Last waren immer eine zentrale Stärke und ein Unterscheidungsmerkmal.Eines der großen Probleme in der Softwareentwicklung ist, Entwickler davon abzuhalten, Komplexität hinzuzufügen oder ihr Können demonstrieren zu wollen. Besonders Entwickler am Anfang ihrer Laufbahn neigen dazu, Lösungen übermäßig zu verkomplizieren, statt ein Problem geradlinig, wenn auch weniger elegant und ausführlicher, zu lösen und weiterzumachen.
Gos aktuelle Generics-Unterstützung ist nicht ausgefeilt genug, um das zu tragen, wodurch seltsam erzwungene Einschränkungen entstehen.
Die Ergänzung von
sql.Null[T]ist gut. In neuen Projekten werde ich das vermutlich verwenden.In meiner aktuellen Arbeit hängen wir von
nullaus sqlboiler [0] ab, dessen API sehr ähnlich ist. Es verhält sich wiesql.Null, hat aber zusätzlich eine MethodeIsSet() bool, die angibt, ob ein Wert explizit gesetzt wurde, und dabei hilft, „absichtliches null“ von „null, weil nicht initialisiert“ zu unterscheiden. Klingt gut, aber in der Praxis habe ich es noch nie benutzt.Ich freue mich darauf, mir den Mux der Standardbibliothek wieder anzusehen und vielleicht sogar chi zu entfernen. Es ist wirklich schön, wenn solche Funktionen in die Standardbibliothek kommen.
Wenn solche Funktionen in die Standardbibliothek kommen, ist gewährleistet, dass sie immer gepflegt werden und viele Go-Programme denselben Ansatz verwenden.
Wenn man Go in Produktion einsetzt, frage ich mich, ob man schnell auf die neueste Version wechseln kann oder ob man an ältere Releases gebunden bleibt.
Bei öffentlichen Projekten sieht es oft so aus, als würden relativ neue Features vermieden. Auch wenn man keine Generics verwendet, würde ich statt des alten
interface{}gern das mit Go 1.18 eingeführteanynutzen; ich habe auch gehört, Letzteres sei „idiomatischer“.Diesmal war es etwas verzwickt: Das Go-Team hat 1.22 veröffentlicht, bevor wir den Standard auf 1.21 angehoben hatten. Inzwischen ist das Warten fast nur noch Gewohnheit; es ist selten, dass ein Go-Release etwas kaputt macht.
Bei Generics haben wir zum Beispiel etwa sechs Monate gewartet, bevor wir sie tatsächlich genutzt haben, um sicherzustellen, dass Nutzen, Developer Experience sowie Auswirkungen auf Build- und Testgeschwindigkeit nicht gravierend sind.
Wir liefern aber auch Endnutzer-Apps in Go aus, und dort müssen wir wegen der Kompatibilität mit älteren Client-Betriebssystemen bei 1.20 bleiben. Da es in 1.21 einen erheblichen Schnitt gab, bleiben wir in diesem Bereich wahrscheinlich noch einige Jahre auf 1.20.
latest-Container von Go, und die Testsuite ist ziemlich solide, sodass Updates praktisch automatisch erfolgen. Wenn etwas nicht passt, bricht die Pipeline, und dann entscheiden wir, ob wir auf die letzte funktionierende Version pinnen oder das Update aufschieben, bis die Breaking Change behoben ist.Etwa alle paar Jahre zeigt sich zwischen einer neuen Version und einem Third-Party-Paket ein subtiler Edge-Case-Bug; solche Bugs haben per Definition aber die Testsuite umgangen, wären also wahrscheinlich auch bei einem manuellen Upgrade nicht aufgefallen.
Wenn dir die Unterstützung alter Go-Compiler Sorgen macht, kannst du Build Conditions für alte Versionen setzen und Fehlendes definieren. Zum Beispiel kannst du unter
// +build !go1.7eintype any = interface{}platzieren.