Heim  >  Artikel  >  Backend-Entwicklung  >  Babyschritte mit Go

Babyschritte mit Go

WBOY
WBOYOriginal
2024-08-05 19:28:13402Durchsuche

Baby steps with Go

Ich beschloss, Go auf meiner Reise auszuprobieren, um eine neue Sprache zu lernen, die für meine Karriere und meine Interessen nützlich sein würde. Dieses Mal habe ich es mit Go versucht. Ich denke, was den ersten Eindruck betrifft, ist es ziemlich schön.

Dies ist keine geführte Tour und wohl auch nicht für jemand anderen als mich selbst geschrieben, als persönliche Erinnerung.

Ich habe mir dafür ein kleines Projekt namens Os-Release-Q gegeben. Meine Absicht war es, auf jedem von mir verwalteten System eine Binärdatei zu haben, sodass ich genau die Informationen ausdrucken kann, die ich benötige, ohne sie analysieren oder mit den Augen danach suchen zu müssen.

Erste Hürde: Import

Bei der Suche im Internet geht es viel um das Importieren der Pakete anderer Leute, aber sehr wenig um die Organisation des eigenen Codes. Sogar die Dokumente konzentrieren sich eher auf Go Get als auf die Trennung von Bedenken.

Ich stoße in jeder Sprache ziemlich häufig auf diese Hürde, da jede ihre eigene, eigenwillige Philosophie hat, wie man damit umgeht und welche Einschränkungen jede hat oder auferlegt.

Von all den Aktivitäten, die ich unternommen habe, um die Grundlagen zu erlernen, da ich überwiegend einen Python-Hintergrund habe, dauerte es am längsten, bis ich Antworten auf die Frage bekam, meinen Code in mehrere Dateien aufzuteilen. Zusammenfassend habe ich Folgendes gefunden:

  • Die oberste Ebene benötigt ein go.mod, das den Modulnamen des Moduls deklariert
  • Ich kann dann ein src/-Verzeichnis auf der obersten Ebene und ein src/main.go festlegen, in dem ich meine Hauptfunktion platzieren kann, mit einer Paket-Hauptdeklaration oben
  • Das Einfügen von Code in andere Dateien ist so einfach wie das Erstellen einer Datei wie src/others.go mit einer Pakethauptdeklaration.
  • Alle Funktionen und Variablen werden direkt in jeder anderen Datei des Pakets main verfügbar, die Dateien müssen jedoch beim GO-Build-FILES-Aufruf explizit angegeben werden

Bei lokalen Submodulen muss sich das Submodul in einem Ordner befinden. Es kann einen Paket-Submodulnamen deklarieren.

Angenommen, es befindet sich in src/submod/, mit dem Hauptimplementierer in src/submod/submod.go. In main.go importieren wir „module-name/src/submod“ (wobei der Modulname aus go.mod gezogen wird). Und dann können wir submod.SomeFunction().

aufrufen

Wir beachten, dass Submodulfunktionen nur für Importeure verfügbar sind, wenn ihr Name mit einem Großbuchstaben beginnt. Also kein submod.myFunction() – es muss submod.MyFunction() sein.

Es gibt sicherlich noch andere Überlegungen zu Submodulen und Importen, aber was die Organisation und Trennung des Codes angeht, ist dies das Wesentliche.

Um die Dinge vernünftig zu halten, habe ich versucht, nur eine Datei zu haben, die das Hauptpaket deklariert, und den Rest in Submodule zu isolieren – diese werden automatisch importiert, ohne dass sie in der Dateiliste von go build FILES deklariert werden müssen.

Grundlegende Aufgaben erledigen

Nachdem ich diese Besonderheit von Go geklärt hatte, ergab sich der Rest ganz einfach. Für jede grundlegende Aufgabe gab es natürlich einen StackOverflow-Eintrag oder eine GoByExample.com-Seite und im Grunde genommen die Go-Sprachreferenz.

  • String-Handling erfolgt über das Strings-Paket
  • Array-Handhabung verfügt über eine Reihe nativer Funktionen, darunter das Muster „base_array = append(base_array, item1, item2)“ – es funktioniert auch zum Erweitern eines Arrays mit den Werten eines anderen über append(base, other_array...)
  • Die Fehlerbehandlung erfolgt normalerweise, aber nicht unbedingt, durch die Weitergabe von Fehlerobjekten.
  • Es gibt eine „Protokoll“-Bibliothek für ein praktisches, vorkonfiguriertes, unkompliziertes Protokoll. Es beinhaltet einen log.Fatal(message)-Aufruf, der einen Fehler protokolliert und sofort beendet.
  • Das Aufrufen von Unterprozessen ist einfach über die „os/exec“-Bibliothek mit dem exec.Command(base, args...)-Muster

Zwei besonders häufige Aufgaben verdienen eigene Absätze.

Fehlerbehandlung

Die grundlegende Fehlerbehandlung wird oft als umständlich bezeichnet, da Fehler buchstäblich mitten im Kontrollfluss behandelt werden müssen. Dies mag für Programmierer, die einen Try/Catch-Workflow nutzen, ein Gräuel sein, aber das Problem an dem Punkt zu behandeln, an dem es auftreten kann, ist nicht so schlimm.

// explicit return item `err` forces us to be aware of it
// but having the ability to check it in the same breath is not so bad
if result, err := someCall(); err != nil {
    log.Fatal("Sorry.")
}

// Equally valid is
/*
result, err := someCall()
if err != nil {
    log.Fatal("Sorry")
}
*/

fmt.Println(result)

Try/Catch-Methode vergleichen

try:
    result = someCall()
    print(result)
except:
    print("Sorry") # a little divorced from potential origin of error
    sys.exit(1)

Argumentparsing

Ich komme nicht umhin, das Gefühl zu haben, dass die Implementierung der Flags-Bibliothek etwas unausgegoren ist. Offensichtlich sind die Menschen daran gewöhnt und damit einverstanden, wenn man bedenkt, dass es in seiner jetzigen Form überlebt.

Der Aufruf des Programms -flag arg1 arg2 gibt uns den Schalter, für den das Flag eingerichtet ist, und positionals := flags.Args() gibt uns das Array von ["arg1", "arg2"] zurück

Der Aufruf des Programms arg1 arg2 -flag schaltet jedoch nicht um, was -flags tun soll, und gibt stattdessen Positionsangaben als ["arg1", "arg2", "-flag"] aus, wobei das Flag wurde nicht analysiert.

Dies kann nützlich sein, um einen Unteraufruf wie das Programm colorize ls -l zu übergeben, bei dem ls -l wörtlich weitergegeben wird – damit ich einen Anwendungsfall erkennen kann.

Es ist nur so, dass die meisten Programme Flag-Argumente überall in der Nähe von Positionselementen zulassen. ls dir1/ -l dir2/ ist dasselbe wie ls -l dir1/ dir2/, und diese Konvention gilt für die überwiegende Mehrheit der Unix- und Linux-Befehle.

Vielleicht ist das einfach gewöhnungsbedürftig – und es lohnt sich, darauf hinzuweisen.

Zweck und Anwendungsfall von Go

Abgesehen vom Dateiimport-Paradigma fand ich es ziemlich einfach, meine Basisanwendung zu implementieren. Alles, was ich falsch gemacht habe, fühlte sich ziemlich offensichtlich an und die Fehler waren bedeutsam. Es fühlt sich wirklich so an, als ob ich mich einfach darauf konzentrieren kann, „Dinge zu erledigen“.

Anhand meiner bisher sehr geringen Nutzungsmenge und unter Berücksichtigung meiner spezifischen Bedürfnisse kann ich sehen

  • einfacher Einstieg
  • kompilierte Binärdatei, keine Laufzeitabhängigkeit
  • Einfache Sprache mit Typen ist ein Fortschritt gegenüber Shell-Scripting
  • angeblich einfache Multiprocessing-Unterstützung

Ich dachte, spärliche Typen anstelle von Objekten und Vererbung wären ein Hindernis, aber soweit so gut. In anderen Sprachen komme ich ohne sie aus. Wenn ich also dazu komme, Schnittstellen und Typen zu definieren, wird es sich wie ein Fortschritt gegenüber Lua und Bash anfühlen. Ich hoffe.

Einer der Gründe, warum ich eine in die native Sprache kompilierte Sprache erforschen wollte, bestand darin, Binärdateien erstellen zu können, die leicht verschoben werden können, ohne dass eine bestimmte Version einer Laufzeitumgebung vorhanden sein muss.

Ein Kollege kam kürzlich bestürzt an meinen Schreibtisch und versuchte, eine Lösung zu finden, um Java 17 auf ein altes Node-Basisimage zu bringen, das auf Debian 10 basierte. Entweder müsste er die Node-Version aktualisieren, um ein neueres Basis-Image zu erhalten, ein neues Debian-Basis-Image verwenden und Node manuell installieren und konfigurieren, oder er müsste das Internet nach einem benutzerdefinierten Repo durchsuchen, das von Gott weiß wer gehostet wird -falls Java 17 gehackt wurde, das unter Debian 10 laufen würde.

Wie viel einfacher wäre es, wenn die bereitgestellte Software keine derart widersprüchlichen Laufzeitabhängigkeiten hätte...

Aus betrieblicher Sicht besteht meiner Meinung nach der einzige große Vorteil darin, dass ich problemlos Code schreiben und eine ELF-Binärdatei erstellen kann, um sie dann auf dem „beliebigen System X“ bereitzustellen, ohne mich damit auseinandersetzen zu müssen Sicherstellen, dass die richtige Version einer bestimmten Laufzeit vorhanden ist, und Verwalten widersprüchlicher Abhängigkeiten.

Ich bin mir sicher, dass es noch weitere Vorteile gibt, und ich habe viel über die Benutzerfreundlichkeit von Multithreading und Multiprocessing in Go gehört, und ich habe vor, als nächsten Schritt ein Miniprojekt auszuarbeiten, um das zu erkunden – wahrscheinlich etwas, das auf Eingaben auf mehreren Kanälen lauscht und als Reaktion darauf einige grundlegende Aufgaben ausführt. Ich hatte in einigen Testautomatisierungsaufgaben, die ich zuvor hatte, einen Anwendungsfall dafür, daher ist es mir zu diesem Zeitpunkt nicht fremd.

Das obige ist der detaillierte Inhalt vonBabyschritte mit Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn