- Go 1.21 erweitert die GODEBUG-basierte Kompatibilität, damit die neue Toolchain das Verhalten älterer Go-Versionen möglichst stabil nachbildet, und konzentriert sich darauf, die Hürde bei Upgrades zu senken
- Go verspricht seit Go 1 im Jahr 2012 Quellcode-Kompatibilität und hat mit Prüfungen der öffentlichen API und umfangreichen internen Tests Schäden durch Entfernen oder Änderungen reduziert
- Selbst dokumentationsgemäß zulässige Verbesserungen wie höhere Präzision bei
time.Now, Änderungen an der sort-Implementierung, andere Ausgaben von compress/flate, erweiterte Eingaben für strconv.ParseInt oder geändertes Parsing bei net.ParseIP können bestehende Programme beschädigen
- Seit Go 1.21 bleiben GODEBUG-Einstellungen für Kompatibilität mindestens 2 Jahre oder 4 Go-Releases erhalten, und abhängig von der
go-Version in go.mod kann das frühere Verhalten als Standardwert beibehalten werden
- Go 2 wird nicht als neue Spezifikation erscheinen, die Go-1-Programme kaputtmacht; auch bei neuen Features setzt Go Kompatibilität zuerst, damit Upgrades der Toolchain stabil bleiben
Grundprinzipien der Go-1-Kompatibilität
- Go hat mit Go 1 im Jahr 2012 im Dokument „Go 1 and the Future of Go Programs“ das Ziel festgelegt, dass Programme, die der Go-1-Spezifikation entsprechen, während der gesamten Lebensdauer dieser Spezifikation ohne Änderungen weiter kompiliert werden und korrekt laufen sollen
- Im Zentrum dieses Versprechens steht die Quellcode-Kompatibilität
- Beim Update auf eine neue Go-Version muss der Code neu kompiliert werden
- Neue APIs dürfen hinzugefügt werden, aber Ergänzungen, die bestehenden Code beschädigen, sollen vermieden werden
- Es kann nicht garantiert werden, dass jede künftige Änderung niemals irgendein Programm beschädigt
- Wenn ein Programm vom Verhalten eines Bugs abhängt, kann es beim Beheben dieses Bugs kaputtgehen
- Go versucht, Beschädigungen so weit wie möglich zu reduzieren und stabile Upgrades zu erhalten
Verhindern von Kompatibilitätsbrüchen durch Prüfungen der öffentlichen API
- Im Entwicklungsprozess von Go wird die Liste der öffentlichen APIs jedes Pakets in separaten Dateien neben den eigentlichen Paketen verwaltet
- Zum Beispiel verzeichnet
go/api/go1.21.txt Einträge zu Funktionen, Methoden und Typen aus bytes, cmp, context und anderen
- Standardtests prüfen, ob die tatsächliche Paket-API mit diesen Dateien übereinstimmt
- Wenn eine neue API hinzugefügt wird, muss sie auch in die API-Datei eingetragen werden, damit die Tests bestehen
- Werden bestehende APIs geändert oder entfernt, schlagen die Tests ebenfalls fehl
- Nicht nur das Entfernen von APIs, auch Typänderungen können die Kompatibilität brechen
os.Stdout ist eine globale Variable vom Typ *os.File
- Würde man sie in ein Interface mit denselben Methoden ändern, würde Code wie
greet(f *os.File) kaputtgehen, der explizit *os.File erwartet
- API-Prüfungen sind nützlich, um Änderungen oder Entfernen von APIs zu erkennen, verhindern aber nicht alle möglichen inkompatiblen Änderungen in Go
Subtile Brüche, die Tests sichtbar machen
- Entwicklungsversionen neuer Go-Releases werden fortlaufend gegen den gesamten internen Go-Code von Google getestet
- Wenn die Tests erfolgreich sind, wird der betreffende Commit in Googles produktive Go-Toolchain übernommen
- Wenn interne Tests fehlschlagen, wird davon ausgegangen, dass auch externer Code betroffen sein könnte, und es werden Wege gesucht, die Auswirkungen zu verringern
- In den meisten Fällen wird die Änderung zurückgenommen oder so umgeschrieben, dass Programme nicht kaputtgehen
- Manche Änderungen bleiben trotz möglicher Brüche bestehen, wenn sie wichtig und laut Dokumentation kompatible Änderungen sind
- Auch dann wird versucht, den Wirkungsbereich zu verringern, und in den Release Notes werden potenzielle Probleme festgehalten
Zwei Fälle, die in Go 1.1 sichtbar wurden
-
Struct-Literale und neue Felder
- In Go 1 war
net.TCPAddr ein Struct mit den zwei Feldern IP und Port, und zusammengesetzte Literale ohne Feldnamen ließen sich kompilieren
- Als in Go 1.1 das Feld
Zone zu net.TCPAddr hinzugefügt wurde, ließ sich bestehender Code mit dem Fehler „too few initializers in struct literal“ nicht mehr kompilieren
- Die kompatible Schreibweise ist die Verwendung von benannten Literalen
var myAddr = &net.TCPAddr{
IP: net.IPv4(18, 26, 4, 9),
Port: 80,
}
- Wenn
Zone nicht angegeben wird, verwendet das Feld den Zero Value, also einen leeren String
- Die Kompatibilitätsdokumentation fordert für Structs der Standardbibliothek zusammengesetzte Literale mit Feldnamen, und
go vet meldet Literale ohne Feldnamen, bei denen diese Form für die Kompatibilität mit späteren Versionen nötig ist
-
Zeitpräzision
- Nach Go 1 wurde
time.Now so geändert, dass statt Mikrosekundenpräzision Nanosekundenpräzision zurückgegeben wird
- Diese Änderung konnte Tests beschädigen, die erwarten, dass ein mit
save und load round-getrippter time.Now-Wert identisch bleibt
- Wenn die gespeicherte Darstellung nur Mikrosekundenpräzision erhält, kann derselbe Test unter Go 1 erfolgreich sein, unter Go 1.1 aber fehlschlagen
- Um das Anpassen solcher Tests zu erleichtern, hat Go die Methoden
Round und Truncate hinzugefügt und in den Release Notes die möglichen Probleme sowie die neuen Methoden dokumentiert
- Die verbesserte Präzision war ein besseres Verhalten und lag innerhalb des dokumentierten Rahmens der Funktion; deshalb wurde die Änderung trotz möglicher Brüche veröffentlicht
Drei Arten von Änderungen, die Kompatibilität brechen können
-
Änderungen an der Ausgabe
- Ausgabeänderungen treten auf, wenn eine Funktion andere Ausgaben als zuvor liefert, die neue Ausgabe aber mindestens genauso korrekt oder sogar korrekter ist als die alte
- Die Nanosekundenpräzision von
time.Now ist ein typisches Beispiel
- In Go 1.6 wurde die
sort-Implementierung um etwa 10 % beschleunigt, wodurch sich die Reihenfolge von Elementen ändern konnte, die als gleichwertig gelten
- Ausgabe in Go 1.5:
[red blue green white black yellow orange indigo violet]
- Ausgabe in Go 1.6:
[red blue white green black orange yellow indigo violet]
- Das Sortieren darf dieselbe Ergebnismenge in beliebiger Reihenfolge zurückgeben, aber Programme, die eine bestimmte Reihenfolge erwarteten, gingen kaputt
- In Go 1.8 wurde
compress/flate so verbessert, dass bei ähnlichem CPU- und Speicher-Overhead kleinere Ausgaben erzeugt werden
- Dadurch konnten reproduzierbare Archiv-Builds innerhalb von Google bestehende Archive nicht mehr exakt nachbilden
- Das betroffene Projekt forkte
compress/flate und compress/gzip, um den früheren Algorithmus beizubehalten
- Gegen Ausgabeänderungen hilft es, Programme und Tests so zu schreiben, dass alle gültigen Ausgaben akzeptiert werden
- Wenn wirklich reproduzierbare Ausgabe erforderlich ist, kann man den Code forken, verliert damit aber auch den Anschluss an Bugfixes
-
Änderungen an der Eingabe
- Eingabeänderungen treten auf, wenn sich akzeptierte Eingaben oder deren Verarbeitung ändern
- Go 1.13 führte zur besseren Lesbarkeit von Zahlen die Unterstrich-Syntax ein, und
strconv.ParseInt wurde angepasst, diese neue Syntax zu akzeptieren
- Dadurch ging Code eines externen Nutzers kaputt, der durch Unterstriche getrennte Zahlen als separates Datenformat verwendete
- Dieser Code versuchte zuerst
ParseInt und behandelte Unterstriche nur dann separat, wenn das Parsen fehlschlug; da ParseInt nun nicht mehr fehlschlug, änderte sich das Verhalten
net.ParseIP akzeptierte nach frühen Beispielen in IP-RFCs dezimale IP-Adressen mit führenden Nullen
- Go las
18.032.4.011 als 18.32.4.11
- BSD-basierte C-Bibliotheken interpretierten führende Nullen als Beginn einer Oktalzahl und lasen denselben String als
18.26.4.9
- In Go 1.17 wurde
net.ParseIP so geändert, dass führende Nullen vollständig abgelehnt werden
- Ziel war, dass Go und C dieselbe Bedeutung erhalten, wenn beide das Parsen einer IP-Adresse erfolgreich durchführen
- Kubernetes befürchtete, dass bestehende gespeicherte Konfigurationen unter Go 1.17 nicht mehr geparst werden könnten, und begann daher, einen Fork des ursprünglichen
net.ParseIP zu verwenden
- Bei Benutzereingaben ist es besser, vor dem Parsen zunächst die gewünschte Syntax zu validieren; in manchen Fällen kann jedoch ein Fork des Codes nötig sein
-
Protokolländerungen
- Protokolländerungen liegen vor, wenn sich eine Paketänderung in der Kommunikation mit der Außenwelt auf Protokollebene sichtbar auswirkt
- Go 1.6 fügte automatische Unterstützung für HTTP/2 hinzu
- Ein Go-1.5-Client verwendet nur HTTP/1.1 und kann deshalb in bestimmten Umgebungen mit Netzwerk-Middleboxes normal funktionieren
- Nach dem Update auf Go 1.6 wird HTTP/2 verwendet, und wenn HTTP/2 in dieser Umgebung nicht funktioniert, kann das Programm kaputtgehen
- Go möchte moderne Protokolle standardmäßig unterstützen, aber schon die Aktivierung von HTTP/2 kann Programme beschädigen, ohne dass das Programm oder Go selbst einen Fehler hat
- Go 1.6 dokumentierte die Änderung in den Release Notes und bot Wege, HTTP/2 zu deaktivieren
- Das Feld
TLSNextProto explizit setzen
GODEBUG=http2client=0, GODEBUG=http2server=0 oder beides setzen
- Die Unterstützung für SHA1-basierte HTTPS-Zertifikate ist ein noch subtileres Beispiel für eine Protokolländerung
- Zertifizierungsstellen stellten 2015 die Ausgabe von SHA1-Zertifikaten ein, und wichtige Browser akzeptierten sie ab 2017 nicht mehr
- Go 1.18 deaktivierte die Unterstützung für SHA1-Zertifikate standardmäßig und erlaubte einen Workaround per GODEBUG
- Einige Kubernetes-Installationen verwenden weiterhin private SHA1-Zertifikate, weshalb Go die Workaround-Einstellung länger als ursprünglich geplant beibehalten will
Erweiterte GODEBUG-Unterstützung in Go 1.21
- Go 1.21 erweitert und formalisiert die Nutzung von GODEBUG, um auch subtile Kompatibilitätsprobleme zu reduzieren
- Für Änderungen, die nach den Go-1-Kompatibilitätsregeln zulässig sind, aber bestehende Programme beschädigen können, wird eine GODEBUG-Einstellung definiert, mit der einzelne Programme das neue Verhalten ablehnen können
- Fälle, in denen das Hinzufügen einer solchen Einstellung nicht möglich ist, kann es geben, sie gelten aber als sehr selten
- GODEBUG-Einstellungen für Kompatibilität bleiben mindestens 2 Jahre, also 4 Go-Releases, erhalten
- Einstellungen wie
http2client und http2server können deutlich länger, in manchen Fällen unbegrenzt, bestehen bleiben
- Wo immer möglich, wird jede GODEBUG-Einstellung mit einem
runtime/metrics-Zähler verknüpft
- Der Zählername hat das Format
/godebug/non-default-behavior/<name>:events
- Wenn zum Beispiel
GODEBUG=http2client=0 gesetzt ist, zählt /godebug/non-default-behavior/http2client:events die Anzahl der HTTP-Transports, die ohne HTTP/2 konfiguriert wurden
- Die Standardwerte für GODEBUG eines Programms richten sich nach der in
go.mod des Main-Pakets angegebenen Go-Version
- Wenn in
go.mod go 1.20 steht und auf die Go-1.21-Toolchain aktualisiert wird, behalten die in Go 1.21 geänderten, per GODEBUG steuerbaren Verhaltensweisen weiter das Go-1.20-Verhalten bei, bis go.mod auf go 1.21 geändert wird
- Einzelne GODEBUG-Einstellungen lassen sich mit einer
//go:debug-Zeile in package main ändern
- Eine zentrale Liste dokumentiert alle GODEBUG-Einstellungen
Der Fall panic(nil)
- In Go 1.21 löst
panic(nil) nun einen Runtime-Panic aus, der nicht nil ist
- Diese Änderung sorgt dafür, dass das Ergebnis von
recover zuverlässig anzeigt, ob die aktuelle Goroutine sich in einem Panic befindet
- Das neue Verhalten wird über eine GODEBUG-Einstellung gesteuert und hängt von der
go-Zeile in go.mod des Main-Pakets ab
- Bei
go 1.20 oder älter bleibt panic(nil) weiter erlaubt
- Bei
go 1.21 oder neuer wird panic(nil) zu einem Panic mit runtime.PanicNilError
- Der versionsbasierte Standardwert kann explizit überschrieben werden, indem in
package main folgende Zeile ergänzt wird
//go:debug panicnil=1
- Diese Kombination ermöglicht es, auf eine neue Toolchain zu aktualisieren und gleichzeitig das Verhalten der früheren Toolchain beizubehalten, nur die benötigten Einstellungen fein granular zu steuern und per Produktions-Monitoring zu erkennen, ob nicht standardmäßiges Verhalten genutzt wird
- Weitere Details sind in „Go, Backwards Compatibility, and GODEBUG“ zusammengefasst
Go 2 bricht Go 1 nicht
- Das Dokument „Go 1 and the Future of Go Programs“ enthielt den Hinweis, dass eines Tages eine Go-2-Spezifikation erscheinen könnte
- Go 2 im Sinne einer Version, die Go-1-Programme nicht mehr kompiliert, wird nicht kommen
- Go 2 im Sinne einer größeren Weiterentwicklung von Go 1, die 2017 begann, hat bereits stattgefunden
- Go bewertet Kompatibilität als deutlich wertvoller als einen Bruch mit der Vergangenheit und hat sich für einen Weg entschieden, der die Kompatibilität weiter stärkt
- Auch in Zukunft wird es neue und spannende Arbeit geben, sie wird aber mit Vorsicht und kompatibel umgesetzt, damit Upgrades zwischen Toolchains so stabil wie möglich bleiben
1 Kommentare
Meinungen auf Hacker News
Die wichtige Frage bei Kompatibilität ist nicht „ob“, sondern „wie“. Eigentlich geht es weniger um Rückwärtskompatibilität als darum, dass man möchte, dass der eigene Code ab jetzt einfach weiter funktioniert.
Go 1.21 bietet zwei zentrale Dinge, die man in anderen Sprachökosystemen nur schwer gleichzeitig findet: Für jede Änderung gibt es eine GODEBUG-Einstellung, Änderungen lassen sich einzeln zurückdrehen, und es gibt auch Metriken, um zu erkennen, ob die alte Implementierung verwendet wird. Außerdem gibt es pro Modul eine Toolchain-Version, und ältere Go- sowie neuere Go-Toolchains können sicher und automatisch wie Module bezogen werden.
Als Bonus gilt: Wenn man eine bestimmte Version wie
go 1.21.2angibt, wendet Go auch bei Ausführung mit einer neueren Go-Version automatisch die zugehörigen Opt-out-Konfigurationen an, bis man das neue Verhalten ausdrücklich anfordert. Das lässt sich im Code, ingo.mododer in Umgebungsvariablen deklarieren und ist eine einfache, schöne Lösung, die fast alle Kompatibilitäts-Use-Cases von Entwicklern bis zu Distributoren abdeckt.use v5.24am Dateianfang dafür sorgen, dass es sich wie Perl 5.24 verhält; das gilt auf Datei- statt auf Modulebene.Schon allein die Frage, ob man eine neue Version unterstützt oder nicht, erzeugt absurd komplexe Probleme; dazu entsteht eine tiefe Schlucht, in der „wir glauben, die neue Version zu unterstützen, und die neue Version glaubt, uns zu unterstützen, aber beide übersehen etwas“.
Ich finde diese Richtung wirklich gut. Es gibt kaum etwas Besseres, als in eine Go-Codebasis zu kommen und erwarten zu können, dass alles einfach funktioniert, wenn man nur die Go-Version anhebt.
Allerdings macht mir Sorgen, dass sich das Typsystem nur schwer deutlich verbessern lässt, ohne Breaking Changes nach dem Muster „das war fehlerhafter Code und kompiliert jetzt nicht mehr“ einzuführen. Ich weiß nicht, ob das Go-Team daran interessiert ist, aber es gibt viele niedrig hängende Früchte, mit denen man die Robustheit zur Compile-Zeit stark erhöhen könnte, ohne neue Sprachfeatures hinzuzufügen.
Beispiele wären Meldungen über ungeprüftes
nil, Prüfungen von Array-Zugriffen, Typinferenz für verschachtelte Struct-Literale und Exhaustiveness-Checks für Enums. Besonders beim Schreiben verschachtelter gRPC-Aufrufe bräuchte man nur eine Stufe Typinferenz, weil der Typ bereits in der Signatur der aufgerufenen Funktion steht; in Go ist das unnötig schmerzhaft.Wenn das Go-Typsystem verbessert wird, wäre es schön, wenn solche einfachen und eleganten algebraischen Datentypen hinzukämen. Sie würden wirklich gut zu Go passen; Go sollte sich dieses Geschenk machen.
Stimme voll und ganz zu. Der Grund, warum ich mich auf Go-Releases freue, ist, dass oft nützliche Dinge hinzukommen, ohne dass irgendetwas kaputtgeht.
Kein Teil der Sprache wirkt so kaputt, dass er sich nicht durch eine rückwärtskompatible Änderung beheben ließe. Selbst für die wenigen Fallstricke wie die Zuweisung von Schleifenvariablen gibt es meist Vorschläge, die die Kompatibilität wahren.
Persönlich kann ich die Erwartungshaltung gegenüber Go-Releases nachvollziehen, aber der Unterschied in der Haltung der beiden Ökosysteme ist interessant.
Einige der Brüche, die ich früher erlebt habe, habe ich einmal zusammengefasst: https://news.ycombinator.com/item?id=29763324
Bei Rust oder gcc bekommt man durch ein Compiler-Upgrade neue Sprachfeatures, aber die meisten komplexen Bibliotheken bleiben unverändert und können separat aktualisiert werden. Rusts HTTP ist zum Beispiel als
hypergetrennt, C nutztlibcurlund Ähnliches.Bei Go hingegen bekommt man beim Compiler-Upgrade nicht nur Generics,
embedund Toolchain-Funktionen, sondern zugleich Änderungen antls, Sicherheitsbibliotheken sowie HTTP-Client und -Server. Große Standardbibliotheksblöcke wiehttp,tlsundcryptohätten viel besser separate Bibliotheken sein sollen. Dann könnte man den Compiler ohne Angst aktualisieren und die Bibliotheken in eigenem Tempo nachziehen.asyncein; in der Edition 2015 konnte man aber Variablen oder Funktionen namensasyncerstellen, also war das ein Breaking Change.Rückwärtskompatibilität ist gut, aber ich bin nicht sicher, ob eine Zukunft in Ordnung wäre, in der Go wegen Kompatibilitätsbrüchen irgendeine Innovation X nicht umsetzen kann.
Ich unterstütze die Haltung sehr, dass ein künftiges Go 2 die Kompatibilität zu Go 1 niemals brechen wird. Wenn man die Sprache in diesem Ausmaß ändern müsste, wäre es meiner Meinung nach besser, sie einfach zu forken und umzubenennen.
Allerdings frage ich mich, warum man nicht noch weiter geht und sagt: „Es wird kein Go 2 geben“, um die Mehrdeutigkeit zu beseitigen. Wenn ein theoretisches Go 2 alle Go-1-Programme ausführt, worin unterscheidet es sich dann von einem Go-1.xx-Release? Der Artikel sagt: „Es wird kein Go 2 geben, das Go-1-Programme bricht“, scheint aber nicht darüber hinauszugehen.
https://github.com/golang/proposal/blob/d661ed19a203000b7c54...
Dort wird erklärt: Wenn der obige Prozess wie geplant funktioniert, wird es in einem wichtigen Sinn kein Go 2 geben, sondern einen langsamen Übergang durch neue Sprach- und Bibliotheksfunktionen. Irgendwann könnte man es aus Marketinggründen „jetzt Go 2“ nennen, man könnte es aber auch einfach überspringen. Die Logik lautet: Es gab kein C 2.0, warum braucht Go dann ein Go 2.0?
Auch populäre Sprachen wie C, C++ und Java sind im Grunde immer bei 1.N-Versionen geblieben, und ich denke, Go sollte dem folgen. Ein echtes Go 2 im Sinne einer neuen, inkompatiblen Sprache oder Kernbibliothek wäre für Nutzer keine gute Option und könnte schädlich sein.
Letztlich ergibt dieser Ansatz meiner Meinung nach Sinn.
Wenn es ein neues Sprachfeature gibt, das die Community eindeutig will, und der richtige oder einzige Weg, es einzubauen, ein neues Keyword ist, dann kann dieses Feature wegen der Regel, Quellkompatibilität niemals zu brechen, nie hinzugefügt werden. Bei großen Änderungen an der Sprachsemantik stimme ich zu, aber rückwärtsinkompatible Änderungen sind nicht immer riesige Umbrüche.
Theoretisch ist die Idee gut, dass sich Rückwärtskompatibilität niemals ändert, aber in der Realität gibt es auch sinnvolle Breaking Changes. Es fühlt sich nicht unbedingt wie ein Vorteil an, ein Sprachfeature dauerhaft mitzuschleppen, bei dessen erstem Design X oder Y nicht berücksichtigt wurde.
Ich nutze Go viel, und diese Richtung wärmt mir wirklich das Herz.
Kompatibilität ist aus Sicht eines Sprach-Teams vielleicht nicht besonders spannend. Man muss immer mit einem Fuß fest in der „fernen Vergangenheit“ stehen. Aber für jemanden, der große Go-Systeme warten muss, ist das ein riesiges Geschenk.
Wenn sich etwas nicht richtig einfügen ließ, war es noch nicht richtig, also haben wir es erneut versucht; und wenn es dann immer noch nicht passte, hatten wir das Problem wahrscheinlich noch nicht ausreichend verstanden, also war es richtig, es länger reifen zu lassen.
Es war wirklich großartig, mit Leuten zu arbeiten, die sorgfältig nachdenken und sich bemühen, die richtigen Ideen hineinzubringen. Ich bin dankbar, dass Russ die Rolle des BDFL übernommen hat, und dafür, mit Ian, Rob und Rob gearbeitet zu haben; dadurch bin ich ein deutlich besserer Engineer geworden.
Verwandter Artikel: Forward Compatibility and Toolchain Management in Go 1.21 - https://news.ycombinator.com/item?id=37122932
Der Satz „Langweilig ist gut. Langweilig ist stabil. Langweilig bedeutet, dass man sich darauf konzentrieren kann, die eigene Arbeit zu erledigen, statt sich darum zu kümmern, was sich in Go geändert hat“ trifft bei mir wirklich einen Nerv.
In meinem Hauptjob nutze ich NodeJS und das JS-Ökosystem insgesamt, und das ist wirklich mühsam. Das Ökosystem ist fragmentiert, und alle machen es auf ihre eigene Weise, was es schwer macht, etwas Stabilität hineinzubringen. Ich habe trotzdem weiterhin Freude an dieser Arbeit, aber ich wünschte, es gäbe auch im JS-Ökosystem ein stabiles, modernes Fundament, auf das man vertrauen und auf dem man aufbauen kann.
Viele Tools und APIs sind in Go geschrieben, und dass auf dem Zielsystem keine separate Runtime nötig ist, wird oft als großer Vorteil genannt. Die Sprache wirkt außerdem einfach zu lernen und zu nutzen, die Unterstützung in VSC und GoLand ist gut, und typische Beschwerden wie die Fehlerbehandlung wirken nicht wie entscheidende Mängel.
Ich frage mich, was Go noch braucht, um in den kommenden Jahrzehnten zum Mainstream der Entwicklung zu werden oder zumindest einen großen Teil des Arbeitsmarkts auszumachen. An manchen Orten gilt es schließlich immer noch als Nischensprache.
Das Problem ist, alle auf diese Baseline zu bringen. Wenn man dort angekommen ist, dürfte es deutlich besser werden.
Außerdem umfasst das JS-Ökosystem auch Frontend-UI-Arbeit, und weil die Einsatzfelder so zahlreich sind, lassen sich mehrere Implementierungen nicht vermeiden. In gewisser Weise ist das sogar wünschenswert.
Einen Teil des Codes von Python nach Golang zu übertragen, hat uns beim Skalieren sehr geholfen. Es freut mich wirklich zu hören, dass Go an seiner zentralen Zusage der Rückwärtskompatibilität festhalten will.
IP-Parsing also. Ich frage mich, wie das ursprüngliche BSD-
inet_ntoaeigentlich geschrieben war.sscanfmitatoi/atolund%d/%uparst immer exakt als dezimale Ganzzahl; um so einen seltsamen Effekt zu erzielen, hätte man also wohl%ioder ein 0-basiertesstrtouverwenden müssen.inet_atonsteht, dass es aus 4.3BSD stammt: https://github.com/dank101/4.3BSD-Reno/blob/master/lib/libc/...Auch das frühere
inet_addraus 4.2BSD verwendet dieselbe Logik: https://github.com/dank101/4.2BSD/blob/master/lib/libc/inet/...inet_atonundinet_addrparsen Adressen auf die naheliegendste Weise. Etwas wiestrtouloder insbesonderesscanfzu verwenden, wäre umständlich gewesen. Die Schönheit von C-Pointern liegt darin, dass sie einfache Parsing-Aufgaben sehr leicht machen – vielleicht sogar zu leicht./etc/hosts„aufzuräumen“, indem ich jedes Oktett mit Nullen aufgefüllt habe.Am Ende musste ich zurückgehen und die Nullen wieder entfernen.
Als Sprachdesigner respektiere ich die hier getroffenen Entscheidungen, einschließlich der Entscheidung, kein echtes Go 2 zu machen.
Ich werde mir auch die Techniken abschauen, mit denen Kompatibilität gewährleistet wird.