„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Babyschritte mit Go

Babyschritte mit Go

Veröffentlicht am 23.08.2024
Durchsuche:415

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, der erste Eindruck ist ziemlich schön.

Dies ist keine geführte Tour und wohl auch nicht für jemand anderen als mich selbst geschrieben, zur 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 per Eye-Grape suchen zu müssen.

Erste Hürde: Import

Beim Durchsuchen des Webs 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:

  • 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 Aufruf von go build FILES explizit angegeben werden

Für lokale Submodule 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.

  • Die String-Verarbeitung erfolgt über das Strings-Paket
  • Die Array-Verarbeitung 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 „Log“-Bibliothek für ein praktisches, vorkonfiguriertes, unkompliziertes Protokoll. Es enthält 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-Weg 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 etwas, woran man sich gewöhnen muss – 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“.

Aufgrund meiner bisher sehr geringen Nutzung und unter Berücksichtigung meiner spezifischen Bedürfnisse kann ich sehen

  • einfacher Einstieg
  • kompilierte Binärdatei, keine Laufzeitabhängigkeit
  • Einfache Sprache mit Typen ist eine Weiterentwicklung 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, war die Möglichkeit, Binärdateien zu erstellen, die leicht verschoben werden können, ohne dass ich mich darauf verlassen muss, dass eine bestimmte Version einer Laufzeit vorhanden ist.

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 für jemanden gehostet wird -if-hacked Java 17, 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 ist der einzige große Vorteil, den ich meiner Meinung nach spüren würde, der: Ich kann problemlos Code schreiben und eine ELF-Binärdatei erstellen, um sie dann auf dem „beliebigen System X“ bereitzustellen, ohne mich damit herumschlagen 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.

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/taikedz/baby-steps-with-go-3ibl?1 Bei Verstößen wenden Sie sich bitte an [email protected], um ihn zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3