1 Punkte von GN⁺ 2 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Diese Release verbessert rund um generische Methoden Struct-Literale und Typherleitung und ergänzt zugleich praktische Funktionen in Standardbibliothek, Runtime und Toolchain
  • Durch größenspezialisierte Speicherallokation sinken die Kosten mancher Allokationen unter 80 Byte um bis zu 30 %; bei allokationsintensiven Programmen sind etwa 1 % Leistungsplus und ein Binärgrößenzuwachs von rund 60 KB zu erwarten
  • encoding/json/v2 ist offiziell verfügbar, das bisherige encoding/json nutzt intern ebenfalls v2, und ein Standard-UUID-Paket sowie ML-DSA-Signaturen auf Basis von FIPS 204 kommen hinzu
  • Mit dem goroutineleak-Profil, pprof-Labels in Tracebacks, synctest.Sleep für virtuelle Zeit und einem In-Memory-HTTP-Testserver werden Diagnose und Concurrency-Tests einfacher
  • Ungepufferte Timer-Kanäle, automatisches Leeren von HTTP-Response-Bodys und strengere Beschränkungen für //go:linkname können bestehenden Code beeinflussen, daher ist vor dem Umstieg auf Go 1.27 eine Kompatibilitätsprüfung nötig

Sprache und Typsystem

  • Das Kernstück von Go 1.27, generische Methoden, kann eigene Typparameter deklarieren, die vom Receiver unabhängig sind
    • Zuvor konnten nur Top-Level-Funktionen generisch sein, sodass selbst typgebundene Operationen als Paketfunktionen geschrieben werden mussten
    • Operationen wie Box[T].Map[U], die einen anderen Elementtyp als den Eingabetyp zurückgeben, lassen sich nun als Methoden definieren
    • Interfaces können weiterhin keine Typparameter-Methoden deklarieren, und generische Methoden können auch kein Interface implementieren
  • Für Schlüssel in Struct-Literalen sind nun nicht nur Feldnamen der obersten Ebene, sondern auch gültige Feldselektoren erlaubt
    • Promotete Felder aus eingebetteten Structs lassen sich direkt initialisieren, etwa mit User{ID: 7}
  • Die Typherleitung für Funktionen wird auf Konvertierungen und zusammengesetzte Literale ausgeweitet
    • Bei []func([]int) int{first, last} werden die Typargumente generischer Funktionen aus dem erwarteten Funktionstyp hergeleitet
    • Zuvor war eine explizite Instanziierung wie first[int], last[int] nötig

Runtime und Performance

  • Der Compiler ruft größenspezialisierte Allokationsroutinen auf und senkt damit die Kosten mancher Speicherallokationen unter 80 Byte um bis zu 30 %
    • Für reale allokationsintensive Programme wird ein Gesamt-Performancegewinn von etwa 1 % erwartet
    • Codeänderungen sind nicht nötig, aber die Binärdatei wächst unabhängig von der Workload um etwa 60 KB
    • Deaktivieren lässt sich das mit GOEXPERIMENT=nosizespecializedmalloc, diese Option soll aber in Go 1.28 entfernt werden
  • Drei standardmäßig aktivierte Compiler-Optimierungen kommen hinzu
    • ein Known-Bits-Dataflow-Pass, der Bits verfolgt, die sicher 0 oder 1 sind, und redundante Operationen entfernt
    • Loop-Invariant-Code-Motion, die während einer Schleife unveränderte Berechnungen aus der Schleife herauszieht
    • Kompilierung passender switch-Anweisungen inklusive fallthrough zu Nachschlagetabellen
  • Das experimentelle GOEXPERIMENT=mapsplitgroup ändert das Speicherlayout von Map-Gruppen von KVKVKVKV zu KKKKVVVV; standardmäßig ist es deaktiviert
  • Der Linker verschiebt Typdeskriptoren und itabs in einen eigenen Abschnitt .go.type und entfernt typelinks und itablinks
    • reflect.typelinks gibt dadurch Typen statt Offsets zurück, was Bibliotheken betreffen kann, die per //go:linkname auf dieses Symbol zugreifen

Goroutine-Diagnose und Profiling

  • Module mit go.mod auf Go 1.27 oder neuer zeigen in den Goroutine-Headern von Tracebacks Labels aus runtime/pprof an
    • Mit pprof.Do angehängte Kontexte wie {request: 42} erscheinen in Crash-Dumps, bei SIGQUIT und in der Ausgabe von runtime.Stack
    • Damit sensible Labels nicht in Tracebacks auftauchen, kann weiter GODEBUG=tracebacklabels=0 verwendet werden
  • Der in Go 1.26 experimentelle Goroutine-Leak-Detektor wird als offizielles goroutineleak-Profil übernommen
    • Er führt GC aus, sucht Goroutinen, die etwa an Kanälen oder Mutexen dauerhaft nicht weiterlaufen können, und gibt ihre Stacks aus
    • In Services lässt sich das über den Endpunkt /debug/pprof/goroutineleak sammeln; GOEXPERIMENT ist nicht mehr nötig

Kryptografie und Identifikatoren

  • Das neue Paket crypto/mldsa implementiert mit ML-DSA die Post-Quanten-Signatur aus FIPS 204
    • Die drei Parametersätze MLDSA44, MLDSA65, MLDSA87 balancieren Schlüssel- und Signaturgröße gegen das Sicherheitsniveau aus
    • ML-DSA wird auch zu Schlüsseln und Signaturen in crypto/x509 sowie zu TLS-1.3-Signaturverfahren in crypto/tls hinzugefügt
  • Das neue Standardbibliotheks-Paket uuid erzeugt und parst RFC-9562-UUIDs mit kryptografisch sicheren Zufallswerten
    • uuid.New() wählt eine für allgemeine Zwecke geeignete Variante, NewV4() erzeugt reine Zufalls-UUIDs, NewV7() zeitlich geordnete UUIDs nach Erstellungszeit
    • Zufallsbasierte UUIDs sind vergleichbar, sodass == direkt verwendet werden kann
    • Nach Zeit sortierbare UUID v7 eignet sich gut als Datenbankschlüssel

JSON- und Datenverarbeitungs-APIs

  • encoding/json/v2 und das Low-Level-encoding/json/jsontext sind ohne GOEXPERIMENT=jsonv2 verfügbar
    • Auch das bisherige encoding/json v1 nutzt intern die v2-Implementierung
    • Abgesehen von einzelnen Fehlermeldungstexten bleibt das bisherige Verhalten erhalten, eine Migration ist also nicht nötig
    • Bei Kompatibilitätsproblemen lässt sich mit GOEXPERIMENT=nojsonv2 die ursprüngliche v1-Implementierung wiederherstellen
    • Für Performance sortiert v2 Map-Schlüssel standardmäßig nicht, daher sollte in Tests mit stabilem Ausgabeformat json.Deterministic gesetzt werden
  • strings.CutLast und bytes.CutLast teilen Werte am letzten Trennzeichen
    • Fehlt ein Trennzeichen, geben sie die gesamte Eingabe, einen leeren Nachteil und false zurück
  • Das generische Hasher[T] in hash/maphash definiert Hash- und Gleichheitsstrategie gemeinsam
    • Gleiche Werte müssen zwingend denselben Hash haben
    • Für vergleichbare Typen gibt es ComparableHasher[T], ebenso sind benutzerdefinierte Strategien wie case-insensitive Strings möglich
  • math/big.Int.Divide berechnet Quotient und Rest gemeinsam und wendet explizit einen Rundungsmodus aus Trunc, Floor, Round, Ceil an
    • Je nach Rundungsmodus ändert sich auch der Rest; das ergänzt in Finanz- und Numerik-Code die Grenzen des bisherigen Abschneidens Richtung 0 mit Quo und Mod
  • math/rand/v2.(*Rand).N erzeugt mit einer eigenen *Rand-Quelle bereichsbegrenzte Zufallswerte für beliebige Integer- oder Duration-Typen

Portables SIMD

  • Das experimentelle Paket simd bietet eine SIMD-API unabhängig von der Vektorgröße und wird mit GOEXPERIMENT=simd aktiviert
    • Auf unterstützten Plattformen wird zu echten Hardware-Vektorinstruktionen kompiliert, sonst auf reine Go-Emulation zurückgegriffen
    • Die Anzahl der Lanes von Typen wie Float32s ist nicht fest vorgegeben, sondern folgt der Hardwarebreite des jeweiligen Systems
  • Die Standardbibliothek nutzt intern bereits simd/archsimd-Intrinsics
    • MemHash32, MemHash64 und StrHash der Swiss-Table-Maps wurden auf die neue SIMD-Basis umgestellt

Deterministische Tests

  • testing/synctest.Sleep führt virtuelle Zeit fort und wartet in einem Schritt, bis alle Goroutinen stabil sind
    • Innerhalb einer synctest-Bubble vergeht keine reale Zeit, daher ist selbst eine Wartezeit von 2 Sekunden sofort abgeschlossen
    • Das ersetzt wiederholten Boilerplate-Code aus time.Sleep und synctest.Wait
  • httptest.NewTestServer startet Server über ein virtuelles In-Memory-Netzwerk statt über echte TCP-Ports
    • srv.Client() verbindet sich über einen prozessinternen Pipe direkt mit dem Handler
    • Durch automatische Bereinigung via t.Cleanup ist defer srv.Close() nicht nötig
    • In Kombination mit testing/synctest lassen sich auch HTTP-Roundtrips schnell und deterministisch in virtueller Zeit testen

Netzwerk- und Kompatibilitätsänderungen

  • Kanäle, die von time.After, time.NewTimer, time.NewTicker usw. zurückgegeben werden, sind nun immer synchrone ungepufferte Kanäle
    • Die frühere GODEBUG-Einstellung asynctimerchan, die das alte Pufferverhalten zurückholte, wurde entfernt
  • Bei HTTP/1 liest http.Response.Body.Close ungelesene Bodys bis zu einer konservativen Grenze weiter, um die Wiederverwendung von Verbindungen zu unterstützen
    • Wer sich bisher zum vorzeitigen Abbruch großer Downloads auf Close verlassen hat, muss mit Transport.DisableKeepAlives die Wiederverwendung deaktivieren
  • HTTP/2-Server verstehen Client-Prioritäten nach RFC 9218 und bearbeiten Streams mit hoher Priorität zuerst
    • Mit Server.DisableClientPriority = true lässt sich das bisherige Round-Robin-Verhalten wiederherstellen
  • crypto/x509.SystemCertPool auf Windows und macOS unterstützt SSL_CERT_FILE und SSL_CERT_DIR
    • Sind diese Variablen gesetzt, werden Root-Zertifikate von der Platte gelesen und statt der Plattform-API der Go-Validator verwendet
    • Deaktivieren lässt sich das mit GODEBUG=x509sslcertoverrideplatform=0
  • Die HTTP/2-Implementierung, die bisher in der 12.226 Zeilen langen generierten Datei h2_bundle.go steckte, wurde durch das echte Paket net/http/internal/http2 ersetzt
  • Zu net/http kommen HTTP/3-Hooks als Vorstufe vor externer Freigabe hinzu, und Teile der Testsuite laufen bereits unter HTTP/3
    • Eine öffentlich aufrufbare API gibt es noch nicht, aber die Basis für einen künftigen QUIC-basierten http.Transport ist enthalten

Toolchain

  • go test führt standardmäßig die stdversion-Vet-Prüfung aus, die die Nutzung von Standardbibliotheks-Symbolen erkennt, die neuer sind als die in go.mod deklarierte Go-Version
  • Mit go doc pkg@version lässt sich die Dokumentation einer bestimmten Modulversion abrufen
  • go doc -ex listet ausführbare Beispiele auf; wird ein Beispielname direkt angegeben, wird dessen Quelltext ausgegeben
  • go fix erhält die Modernisierungs-Analyzer atomictypes, embedlit, slicesbackward, unsafefuncs
    • waitgroup wird in waitgroupgo umbenannt, fmtappendf entfällt
  • In Modulen ab Go 1.27 fasst go mod tidy verstreute require-Blöcke zu zwei Blöcken für direkte und indirekte Abhängigkeiten zusammen und behält Kommentare bei
  • go tool trace -http=:6060 bindet bei alleiniger Portangabe nur an localhost; für externen Zugriff muss eine Adresse explizit angegeben werden
  • Der go-Befehl entfernt die Unterstützung für das Versionsverwaltungssystem Bazaar (bzr)
  • compile, link, asm, cgo, cover, pack unterstützen Response-Dateien im GCC-Format (@file), um Beschränkungen der Kommandozeilenlänge zu umgehen

Unicode und Sicherheitsgrenzen

  • Die Unicode-Daten der Standardbibliothek werden von Unicode 15 auf Unicode 17 aktualisiert
    • Auch in Unicode 16 hinzugekommene Zeichen werden korrekt als Symbole bzw. druckbare Zeichen klassifiziert
  • Ein Problem wurde behoben, bei dem sich mit os.Root.ReadDir und Readdir aus dem Root-Verzeichnis ausbrechen ließ
  • Nicht autorisierte Nutzung von //go:linkname wird weiter erschwert
    • Die Direktive linknamestd markiert linknames, die nur die Standardbibliothek importieren darf
    • Der Linker prüft nun auch den Zugriff per linkname auf Assemblersymbole, daher sollten Programme mit Abhängigkeit von inoffiziellen internen Symbolen frühzeitig getestet werden

1 Kommentare

 
GN⁺ 2 시간 전
Lobste.rs-Kommentare
  • Könnte die beste Release aller Zeiten sein. SIMD, uuid, jsonv2 sind endlich dabei

    • UUID v5 müsste auch noch unterstützt werden, damit ein vollständiger Umstieg möglich ist
  • Etwas am Thema vorbei, aber ein gutes Beispiel dafür, wie Bugs entstehen, wenn man alles als String behandelt und die Verträge und Zusammenhänge zwischen Interface-Methoden nicht berücksichtigt
    Equal ignoriert die Groß-/Kleinschreibung, aber Hash verwendet den in Kleinbuchstaben umgewandelten Wert. Allerdings definieren EqualFold und ToLower nicht dieselbe Äquivalenzrelation
    https://github.com/golang/go/issues/52204
    "\u017F" (ſ, langes s) und "s" sind unter EqualFold gleich, aber ihre jeweiligen ToLower-Ergebnisse unterscheiden sich. Wenn man also "s" einfügt und danach "ſ", wird es wegen des unterschiedlichen Hashes als separater Eintrag eingefügt
    Noch lustiger ist, dass die Dokumentation genau vor diesem Fall warnt
    https://github.com/golang/go/…

  • Schön zu sehen, dass jemand dort weitermacht, wo Anton aufgehört hat

    • Leider wirkt zumindest ein Teil, besonders der Abschnitt zur grundlegenden Nutzung von JSON v2, wie von einem LLM geschrieben. Der Stil gefällt mir nicht, aber der Inhalt ist trotzdem interessant
    • Es scheint allerdings nicht so, als hätte Anton aufgehört. Er hat auch 1.26 geschrieben, daher sieht es eher so aus, als hätte diesmal einfach jemand anderes zuerst veröffentlicht
  • Ich mag Go, aber wenn in jeder Release weiter solche Features dazukommen, frage ich mich, ob die Go-Syntax wirklich elegant genug ist
    func (b Box[T]) Map[U any](f func(T) U) Box[U] {

    • Subjektiv, aber ich finde es nicht schlechter als in anderen Sprachen, die dieses Feature unterstützen. Zig ist am ähnlichsten, und Rust, TypeScript und C++20 verwenden im Allgemeinen den Typ als Container für Methodendefinitionen, wodurch sich die Wiederholung des Empfängertyps über mehrere Methoden hinweg reduziert
      Andererseits hat die Ausführlichkeit von Go und Zig den Vorteil, dass man nicht woanders hinscrollen muss, um die Empfängerdefinition zu finden. An der Aufrufstelle, die man durchaus für wichtiger als die Definition halten kann, sieht es ohnehin fast überall gleich aus, und der Typ muss auch nicht explizit angegeben werden
  • Ich war eine Weile weg von Go, deshalb freue ich mich auf jsonv2. Wenn es aber zum Standard wird, frage ich mich, ob Go damit nicht das lange gepflegte Kompatibilitätsversprechen bricht

    • jsonv2 muss über den separaten Pfad encoding/json/v2 importiert werden und ist daher eine explizite Entscheidung. Das bestehende encoding/json verwendet intern zwar v2, garantiert aber weiterhin das bisherige Verhalten, und jsonv2 bietet auch Flags, um die Sonderfälle von v1 nachzubilden, verletzt also das Kompatibilitätsversprechen nicht
    • Ich verwende in einem Go-1.26-Projekt die jsontext-Bibliothek aus v2 und bin sehr zufrieden damit