====== Go lernen ======
===== go1.22.2 / Ubuntu 24.04.3 LTS =====
[[https://www.linkedin.com/learning/go-lernen-22741633/in-die-moderne-und-vielfaltige-programmiersprache-einsteigen-und-erste-go-projekte-starten|Linkedin: Go lernen]]
==== Einleitung ====
> apt install golang-go
> go version
go version go1.22.2 linux/amd64
> vim hallo_gopher.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Hallo Gopher!")
}
> go build hallo_gopher.go
> ./hallo_gopher
Hallo Gopher!
==== Das Erste Go-Projekt ====
> vim main.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Hallo Go-Projekt!")
}
> go build main.go
> ./main
Hallo Go-Projekt!
> go help build | less
> go fmt main.go
> go doc fmt.Println
package fmt // import "fmt"
func Println(a ...any) (n int, err error)
Println formats using the default formats for its operands and writes to
standard output. Spaces are always added between operands and a newline
is appended. It returns the number of bytes written and any write error
==== Die Datentypen in Go ====
* Einfache Datentypen
* String, für Zeichenketten
* ''string'' in UTF-8-Kodierung
* Boolean, für boolesche Wahrheitswerte
* ''bool'': true oder false
* Zahlen, für von Integer bis Gleitkomma
* ''int32''
* ''int64''
* ''float32''
* ''float64''
* komplexe Zahlen...
* Zusammengesetzte Datentypen
* ''Array'', Sequenz mehrerer Elemente mit fixer Länge
* ''Slice'', Sequenz mehrerer Elemente mit variabler Länge
* ''Map'', Wörterbuch, das Schlüssel auf einen Wert abbildet
=== Variablen und Konstanten ===
...
func main() {
// Deklaration und Zuweisung in langer Form
var s1 string = "eine Zeichenkette"
var i1 int = 123
var b1 bool = true
fmt.Println(s1, i1, b1)
}
...
// Diese Variable ist im ganzen Package verwendbar
var aPackageVar string = "eine globale Variable"
func main() {
fmt.Println("Hallo Go-Projekt!")
}
package main
import (
"fmt"
)
// Diese Konstante ist im ganzen Package verwendbar
const aPackageKons = "eine globale Konstante"
func main() {
const k1 = "eine Zeichenkette"
const k2 = 123
fmt.Println(k1, k2, k2+k2)
fmt.Println(aPackageKons)
}
=== Arrays und Slices ===
Arrays und Slices sind Sequenzen mehrerer Elemente.
Arrays haben eine feste Länge.
Slices haben eine variable Länge, es entspricht einer "Liste" in ''%%C++%%'' oder ''Java''.
package main
import (
"fmt"
)
func main() {
// Array (mit 2 Elementen) initialisieren und füllen
array01 := [2]string{"Element1", "Element2"}
fmt.Println("unverändert", array01)
// ein Element im Array ändern, das erste Element hat den Index "0"
array01[1] = "Fritz"
fmt.Println("verändert", array01)
}
package main
import (
"fmt"
)
func main() {
// Slice mit make initialisieren
slice01 := make ([]string, 2)
slice01[0] = "Anita"
slice01[1] = "Rom"
fmt.Println(slice01)
// Slice mit Werten initialisieren
slice02 := []string{"Otto", "Hamburg"}
fmt.Println(slice02)
// Slice erweitern
slice02 = append(slice02, "Bär", "Berlin")
fmt.Println(slice02)
}
=== Maps für Schlüssel/Werte-Paare ===
package main
import (
"fmt"
)
func main() {
// Map mit make initialisieren
var map01 = make (map[string]int)
map01["Anita"] = 23
map01["Berta"] = 32
fmt.Println(map01)
// Map mit Werten initialisieren
var map02 = map[string]int{"Papa": 54, "Mama": 45}
fmt.Println(map02)
// Wert zu einem Schlüssel abrufen
fmt.Println("Alter von Papa:", map02["Papa"])
}
=== Structs mit Feldern nutzen und definieren ===
In Go können mehrere Werte zu Strukturen ("Structs") zusammengefasst werden.
Mit Structs können Sie Daten kapseln, und das ganz ohne Objektorientierung.
Ein Struct ist ein einfacher Datenkontainer mit Feldern.
package main
import (
"fmt"
)
type Struktur01 struct {
Name string // mit großem Anfangsbuchstaben ist es ein öffentliches Feld
alter int // mit kleinem Anfangsbuchstaben ist es ein privates Feld
}
// Struct einbinden
type Struktur02 struct {
Struktur01 // das Struct "Struktur02" hat nun alle Felder, die das Struct "Struktur01" hat und es können zusätzliche hinzugefügt werden
passwort string
}
func main() {
// normales Struct
c := Struktur01{Name: "Alfred Nobel", alter: 63}
fmt.Println(c)
// ein Struct in ein anderes Struct einbinden
d := Struktur02{passwort: "geheim"}
d.Name = "Drachentöter"
fmt.Println(d)
// leeres Struct; es werden die initialen Werte ("0" und "leer") der Felder in Struktur01 ausgegeben
var leereStruktur Struktur01
fmt.Println(leereStruktur)
}
=== Pointer ===
package main
import (
"fmt"
)
type Struktur01 struct {
Name string // mit großem Anfangsbuchstaben ist es ein öffentliches Feld
alter int // mit kleinem Anfangsbuchstaben ist es ein privates Feld
}
func main() {
// normales Struct
c := Struktur01{Name: "Alfred Nobel", alter: 63}
// Pointer p auf Struct c
var p *Struktur01 = &c
// Pointer mit * auflösen und Wert zuweisen (Überschreiben)
(*p).Name = "Gorilla"
fmt.Println("c:", c)
fmt.Println("Pointer auf c:", p)
}
=== Der leere Wert nil ===
package main
import (
"fmt"
)
type Struktur01 struct {
Name string // mit großem Anfangsbuchstaben ist es ein öffentliches Feld
alter int // mit kleinem Anfangsbuchstaben ist es ein privates Feld
}
func main() {
// Pointer ist nil
var p *Struktur01
// prüfen ob der Pointer p nil ist
fmt.Println("Pointer p ist nil:", p == nil)
// Slice ist nil
var s []float64
fmt.Println("Slice s ist nil:", s == nil)
// Map ist nil
var m map[string]float64
fmt.Println("Map m ist nil:", m == nil)
}
=== Fehler ===
//In Go gibt es keine Spezialbehandlung für Fehler. In Go sind Fehler Werte wie Strings, Integer oder Struts.//
package main
import (
"fmt"
)
func main() {
// Error
err := fmt.Errorf("Benutzer %v wurde nicht gefunden", "Spenzer")
fmt.Println("Error err:", err)
}
//In Go gibt es für Fehler den Typ "error". "error" ist ein gewöhnlicher Go-Datentyp, jedoch mit besonderer Bedeutung.//
==== Den Kontrollfluss strukturieren ====
=== Kontrollstruktur if/else ===
package main
import (
"fmt"
)
func main() {
// if
zweifler := "gehen"
if zweifler != "laufen" {
fmt.Println("Du sollst jetzt nicht laufen.")
}
// if/else
geschwindigkeit := 100
if geschwindigkeit < 120 {
fmt.Println("Du bist zu langsam.")
} else if geschwindigkeit == 120 {
fmt.Println("Die Geschwindigkeit stimmt.")
} else {
fmt.Println("Du bist zu schnell.")
}
}
=== Kontrollstruktur Switch ===
package main
import (
"fmt"
"time"
)
func main() {
// Switch mit Ausdruck
geschwindigkeit := 100
switch {
case geschwindigkeit < 120:
fmt.Println("Du bist zu langsam.")
case geschwindigkeit == 120:
fmt.Println("Die Geschwindigkeit stimmt.")
default:
fmt.Println("Du bist zu schnell.")
}
// Switch über Typ
switch time.Now().Weekday() {
case time.Saturday:
fmt.Println("Es ist Sonnabend.")
case time.Sunday:
fmt.Println("Es ist Sonntag.")
default:
fmt.Println("Es ist ein Arbeitstag.")
}
}
=== for-Schleife ===
package main
import (
"fmt"
)
func main() {
// klassische for-Schleife
for i := 1; i < 7; i++ {
fmt.Println("Durchlauf (for)", i)
}
// while-Schleife
j := 1
for j < 7 {
fmt.Println("Durchlauf (while)", j)
j++
}
// Endlos-Schleife
//k := 1
//for {
// fmt.Println("Durchlauf (while)", k)
// k++
//}
// for-each-Schleife mit range
slice := []string{"Otto", "Didi", "Heinz"}
for index, character := range slice {
fmt.Println(index, "Character", character)
}
}
=== Übung ===
package main
import (
"fmt"
)
func main() {
// Übung: Alter vom Hund in Menschenjahren und Hundejahren
for dogAge := 1; dogAge < 11; dogAge++ {
HumanAge := dogAge * 7
fmt.Printf("Dog: %d, Human %d\n", dogAge, HumanAge)
}
}
==== Funktionen und Methoden ====
=== einfache Funktionen ===
package main
import (
"fmt"
)
func humanAge1(dogAge int) int {
return dogAge * 7
}
func humanAge2(dogAge int) (humanAge int) {
humanAge = dogAge * 7
return
}
func main() {
// Übung: Alter vom Hund in Menschenjahren und Hundejahren
hassoAge := 3
fmt.Println("1. Menschenalter von Hasso", humanAge1(hassoAge))
fmt.Println("2. Menschenalter von Hasso", humanAge2(hassoAge))
}
=== Funktionen mit mehreren Parametern und Rückgaben ===
package main
import (
"fmt"
)
func humanAge(dogAge int) int {
return dogAge * 7
}
func humanAges(dog1, dog2 int) (int, int) {
return humanAge(dog1), humanAge(dog2)
}
func main() {
human1, human2 := humanAges(3, 5)
fmt.Println("Human Ages:", human1, ",", human2)
}
=== Methoden an Strukturen definieren ===
package main
import (
"fmt"
)
// Struct
type Struktur01 struct {
Name string // mit großem Anfangsbuchstaben ist es ein öffentliches Feld
alter int // mit kleinem Anfangsbuchstaben ist es ein privates Feld
}
// Methode
func (c Struktur01) methode01() {
fmt.Println("Ausgabe der Methode:", c.Name)
}
func main() {
// normales Struct
c := Struktur01{Name: "Alfred Nobel", alter: 63}
// Aufruf Methode an Struct
c.methode01()
fmt.Println(c)
}
=== Mit Fehlern in Funktionen umgehen ===
package main
import (
"fmt"
)
// Struktur
type Congressman struct {
Name string
AccountBalance float64
}
// Fehler als Rückgabewert
// Der Kontostand eines bestechlichen Abgeordneten wird von dieser Methode erhöht
// ist ein Abgeordneter nicht bestechlich, wird ein Fehler ausgegeben
func (c Congressman) bestechung(amount float64) error {
// ist der Name des Abgeordneten nicht Peter, so ist er nicht bestechlich
if c.Name != "Peter" {
return fmt.Errorf("%v ist nicht koruppt", c.Name)
}
c.AccountBalance += amount
fmt.Println(c.Name, "hat", c.AccountBalance)
return nil
}
func main() {
//c := Congressman{Name: "Jacjie", AccountBalance: 8000.0}
c := Congressman{Name: "Peter", AccountBalance: 8000.0}
// Fehler behandeln
err := c.bestechung(5000.0)
if err != nil {
fmt.Println("Ein Abgeordneter konnte nicht bestochen werden:", err)
}
fmt.Println(c)
}
=== generische Funktionen ===
package main
import (
"log"
)
// generische Funktion
func humanAge[T int | float64](dogAge T) T {
return dogAge * 7
}
func main() {
// Funktion mit Typ Integer aufrufen
var i int = humanAge(3)
log.Println(i)
// Funktion mit Typ Fließkomma aufrufen
var j float64 = humanAge[float64](4.9)
log.Println(j)
}
=== anonyme Funktionen ===
package main
import (
"fmt"
"net/http"
)
func main() {
// anonyme Funktion definieren und ausführen
func () {
fmt.Println("Hallo Welt")
}()
// anonyme HTTP-Handler-Funktion
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hallo Welt"))
})
http.ListenAndServe(":8080")
}
=== verzögerte Ausführung ===
package main
import (
"fmt"
)
// mit defer kann eine Ausführung verzögert werden
func halloDefer() {
defer fmt.Print("Welt\n")
// defer wird auch bei einem Fehler ausgeführt
// hier greifen wir in einem Slice auf eine Stelle (Index) zu,
// die nicht existiert -> Fehler
//var s = []string{}
//fmt.Println("Huch ", s[42])
fmt.Print("Hallo ")
}
func main() {
// verzögerte Ausführung mit defer
halloDefer()
}
//Typische Anwendungsfälle für ''defer'' sind "Dateien schließen", "Dateien löschen" oder "Speicher freigeben".//
==== Schnittstellen mit Interfaces ====
=== Das Interface ===
package main
import (
"fmt"
)
type Congressman struct {
Name string
}
func (c Congressman) greet() {
fmt.Println("Hallo", c.Name)
}
type Enemy struct{}
func (e Enemy) greet() {
fmt.Println("Go to hell!")
}
// Interface definieren
type Greeter interface {
greet()
}
// Interface nutzen
func passBy(g1, g2 Greeter) {
g1.greet()
g2.greet()
}
func main() {
// Interface
c := Congressman{Name: "Frank"}
e := Enemy{}
passBy(c, e)
}
//Interfaces werden in Go nicht explizit, sondern implizit integriert.//
=== Das Interface Stringer aus der Std-Lib ===
package main
import (
"fmt"
)
type Struktur01 struct {
Name string
}
func (c Struktur01) Sring() string {
return "Hallo! " + c.Name
}
func main() {
c := Struktur01{Name: "Alfred Nobel"}
fmt.Println(c)
}
$ go doc fmt.Stringer
package fmt // import "fmt"
type Stringer interface {
String() string
}
Stringer is implemented by any value that has a String method, which defines
the “native” format for that value. The String method is used to print
values passed as an operand to any format that accepts a string or to an
unformatted printer such as Print.
=== Das leere Interface ===
package main
import (
"fmt"
)
func main() {
// Das leere Interface
var i interface{} = "Hallo"
// Type Assertion heißt in anderen Sprachen Cast -> Typ-Umwandlung
// Type Assertion ohne Prüfung
var s string = i.(string)
fmt.Println(s)
// Type Assertion mit Prüfung
s2, ok := i.(string)
fmt.Println(s2, "ist es ein String?", ok)
// falsche Type Assertion ohne Prüfung erzeugt Panic
var f float64 = i.(float64)
fmt.Println(f)
}
=== Das io.Reader-Interface JSON verarbeiten ===
package main
import (
"fmt"
"strings"
"encoding/json"
)
func main() {
// normales Struct
const jsonBody = `{ "Nachricht": "Hallo Leser!" }`
var jsonMap map[string]string
r := strings.NewReader(jsonBody)
json.NewDecoder(r).Decode(&jsonMap)
fmt.Println(jsonMap)
}
==== Nebenläufig programmieren ====
=== Mit Go-Routinen und Channels nebenläufig programmieren ===
package main
import (
"fmt"
)
func Hallo(gruss string) {
fmt.Println("Hallo", gruss)
}
func main() {
// Go-Routine
go Hallo("Welt")
}
package main
import (
"fmt"
)
// Deklarieren und Initialisieren
//a := make(chan string)
//
// Nachricht über Channel senden
//a <- "Hallo"
//
// Nachricht von Channel empfangen,
// "Pfeil" gibt Richtung des Datenflusses an
//value = <-a
//
//func Hallo(gruss string) {
// fmt.Println("Hallo", gruss)
//}
//
//func main() {
// // Go-Channel
// go Hallo("Welt")
//}
func main() {
// 1. Channel erzeugen
money := make(chan int)
go func () {
// 3. Receive auf Channel money
amount := <-money
fmt.Println("empfangen:", amount, "€")
}()
// 2. an Channel senden
money <- 200
}
package main
import (
"fmt"
"time"
)
func cashflow(money chan int) {
amount := <-money
fmt.Println("empfangen:", amount, "€")
}
func main() {
money := make(chan int)
go cashflow(money)
money <- 1000
time.Sleep(2 * time.Second)
}
package main
import (
"fmt"
"time"
)
func cashflow(money chan int) {
// mit Select kann man gleichzeitig auf mehrere Channels lauschen
select {
case amount := <-money:
fmt.Println("empfangen:", amount, "€")
case <-time.After(1 * time.Second):
fmt.Println("...kein Geld bekommen!!!")
}
}
func main() {
money := make(chan int)
go cashflow(money)
// money <- 1000
time.Sleep(2 * time.Second)
}
package main
import (
"fmt"
"time"
"sync"
)
func worker(id int) {
fmt.Printf("Worker %d startet\n", id)
// durch schlafen wird aufwändige Verarbeitung simuliert
time.Sleep(time.Second)
fmt.Printf("Worker %d ist fertig\n", id)
}
func main() {
// Parallele Worker mit der WaitGroup
var wg sync.WaitGroup
// hier werden 5 Worker in einer Schleife nacheinander gestartet
for i := 1; i <= 5; i++ {
wg.Add(1)
// weil sich alle Worker die Variable "i" teilen, brauchen wir hier noch eine eigene Kopie von "i"
i := i
go func() {
defer wg.Done()
worker(i)
}()
}
// hier warten wir, bis alle Worker fertig sind
wg.Wait()
}
package main
import (
"fmt"
"time"
"math/rand"
)
func speaker(name string, debate chan int) {
for {
microphone := <-debate // auf Mic warten (Nachricht empfangen)
fmt.Printf("Turn %v: %v says '%v'\n", microphone, name, randomAnswer())
time.Sleep(400 * time.Millisecond)
microphone++
debate <- microphone // Mic zurückgeben (Nachricht senden)
}
}
func randomAnswer() string {
answers := []string{"Ich habe Recht", "Nimm dieses Argument.", "Aber ich habe diese Erfahrung gemacht.", "Du ***"}
return answers[rand.Intn(len(answers)-1)]
}
func main() {
debate := make(chan int)
go speaker("Jackie", debate)
go speaker("Frank", debate)
microphone := 1
debate <- microphone
time.Sleep(2 * time.Second)
<-debate
}
==== Praktische Aufgabe: Webserver für Go Proverbs ====
=== Basisprogramm: Webserver Proverbs ===
> go mod init proserve
> go get github.com/jboursiquot/go-proverbs
go: downloading github.com/jboursiquot/go-proverbs v0.0.2
go: added github.com/jboursiquot/go-proverbs v0.0.2
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hallo Proserve!\n"))
})
http.ListenAndServe(":8000", nil)
}
> go build Webserver_Proverbs.go
> ./Webserver_Proverbs &
[1] 274944
> jobs
[1]+ Läuft ./Webserver_Proverbs &
> curl localhost:8000
Hallo Proserve!
> killall Webserver_Proverbs
> jobs
[1]+ Beendet ./Webserver_Proverbs
//Dieses Programm werden wir im folgenden erweitern...//
=== Ersten Plain-Text-API-Endpoint umsetzen ===
package main
import (
"fmt"
"net/http"
"github.com/jboursiquot/go-proverbs"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hallo Proserve!"))
})
// Plain Text Endpunkt
http.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, proverbs.Random().Saying)
})
http.ListenAndServe(":8000", nil)
}
> curl localhost:8000/ ; echo
Hallo Proserve!
> curl localhost:8000/text ; echo
Make the zero value useful.
=== JSON-Endpoint ===
package main
import (
"fmt"
"net/http"
"github.com/jboursiquot/go-proverbs"
"encoding/json"
)
type Proverb struct {
Saying string `json:"saying"`
Link string `json:"link"`
}
func HandleProverbJson(w http.ResponseWriter, r *http.Request) {
randomProverb := proverbs.Random()
p := Proverb {
Saying: randomProverb.Saying,
Link: randomProverb.Link,
}
err := json.NewEncoder(w).Encode(p)
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hallo Proserve!"))
})
// Plain Text Endpunkt
http.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, proverbs.Random().Saying)
})
// JSON Endpunkt
http.HandleFunc("/api", HandleProverbJson)
http.ListenAndServe(":8000", nil)
}
> curl localhost:8000/api ; echo
{"saying":"interface{} says nothing.","link":"https://www.youtube.com/watch?v=PAAkCSZUG1c\u0026t=7m36s"}
=== Unit-Test: JSON-Endpoint ===
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
type TestHandleProverbJson(t *testing.T) {
// 1. HTTP Recorder erstellen
recorder := httptest.NewRecorder()
// 2. HTTP Request
req, _ := http.NewRequest)"GET", "/api", nil)
// 3. HTTP-Händler aufrufen
HandleProverbJson(recorder, req)
// 4. Ergebnis prüfen
if recorder.Code != http.StatusOK {
t.Errorf("fehlerhafter Status %v, erwarteter Status %v", recorder.Code, http.StatusOK)
}
}
=== Fehler behandeln ===
package main
import (
"fmt"
"net/http"
"github.com/jboursiquot/go-proverbs"
"encoding/json"
"log"
)
type Proverb struct {
Saying string `json:"saying"`
Link string `json:"link"`
}
func HandleProverbJson(w http.ResponseWriter, r *http.Request) {
randomProverb := proverbs.Random()
p := Proverb {
Saying: randomProverb.Saying,
Link: randomProverb.Link,
}
err := json.NewEncoder(w).Encode(p)
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hallo Proserve!"))
})
// Plain Text Endpunkt
http.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
_, err := fmt.Fprintf(w, proverbs.Random().Saying)
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
})
// JSON Endpunkt
http.HandleFunc("/api", HandleProverbJson)
err := http.ListenAndServe(":8000", nil)
if err != nil {
log.Fatal(err)
}
}
> ./Webserver_Proverbs &
[1] 293523
> ./Webserver_Proverbs
2026/01/27 16:32:12 listen tcp :8000: bind: address already in use
> killall Webserver_Proverbs
=== HTML-Seite für Proverbs ===
package main
import (
"fmt"
"net/http"
"github.com/jboursiquot/go-proverbs"
"encoding/json"
"log"
"html/template"
)
type Proverb struct {
Saying string `json:"saying"`
Link string `json:"link"`
}
func HandleProverbJson(w http.ResponseWriter, r *http.Request) {
randomProverb := proverbs.Random()
p := Proverb {
Saying: randomProverb.Saying,
Link: randomProverb.Link,
}
err := json.NewEncoder(w).Encode(p)
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
}
func HandleProverbPage(w http.ResponseWriter, r *http.Request) {
t, _ := template.New("page").Parse(`
`)
err := t.Execute(w, proverbs.Random())
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/", HandleProverbPage)
// Plain Text Endpunkt
http.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
_, err := fmt.Fprintf(w, proverbs.Random().Saying)
if err != nil {
http.Error(w, "uups, da ist etwas schief gegangen!", http.StatusInternalServerError)
}
})
// JSON Endpunkt
http.HandleFunc("/api", HandleProverbJson)
err := http.ListenAndServe(":8000", nil)
if err != nil {
log.Fatal(err)
}
}
> ./Webserver_Proverbs &
[1] 296083
> curl localhost:8000/ ; echo
> killall Webserver_Proverbs
=== Das Makefile ===
BINARY = "Webserver_Proverbs"
clean:
rm -f ${BINARY}
build: clean
go build -o ${BINARY} .
==== Das Zen von Go ====
- **einfach**
* Go erreicht viel mit wenig. Ein Programm sollte eine Sache tun, und die gut.
- **bescheiden**
* Unser Kode soll klar, idiomatisch und verständlich sein und seinen Zweck gut erfüllen. Clever ist da hinderlich.
- **Handeln durch Nichthandeln**
* Probleme eliminieren und Umwege vermeiden. Bewusstes Nichthandeln als Alternative zur Problemlösung und Umsetzung nutzen.
==== Ideen für erste Projekte ====
* Go eignet sich besonders für...
* Microservices
* Serverless Funktions
* Kommandozeilen-Tools
* DevOps und Cloud-Automatisierung
===== ...weiter machen... =====
[[https://www.linkedin.com/learning/code-challenges-fur-go/kompakte-programmierratsel-in-go-mit-unterschiedlichen-schwierigkeitsgraden-losen|Code-Challenges für Go]]