Warum go?
Ein Standard, nicht 100 Tools Eco System.
- test system
- module system
go vet
- go routines
go fmt
Ordner Struktur
Leeres Git Repo
go mot init practice
Folder angenommen:
- practice
- go.mod
- app.go
- models
- a.go
- b.go
- routers
- a.go
- b.go
Kann dann so abgerufen werden
import (
"practice/routers"
"practice/models"
...
)
Access zu Methoden
func main() {
http.HandleFunc("/a", models.AHandler)
http.HandleFunc("/b", models.BHandler)
http.ListenAndServe(":8080", nil)
}
Slice
Content für Element setzen
foo[1] = "rofl"
Slice initialisieren mit default werten
var foo []string = []{"foo", "bar"}
var foo []int = []{1, 2, 3}
Ein bestehendes Slice erweitern
s = append(s, "e", "f")
Slice mit Dimensionierung initialisieren
s := make([]string, 3)
Slice mit Custom types initialisieren
type Bar struct {
x, y int
}
var foo []Bar
foo[]Bar{Bar{1,1}}
Länge eines Slice
bestimmen
len(foo)
Wie finde ich einen String in einem Slice
// check if slice contains string
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
strings
Stringmanipluationen aller Art bietet strings
. Zum Beispiel ein Slice
zu
einem String
verwandeln
strings.join(Slice, sep)
Konstanten const
Konstanten werden speziell markiert. Sie bleiben zur Compilezeit gleich und werden niemals verändert.
const (
foo = "lol"
bar = 42
)
oder auch in Funtkionen
func countlines () {
const (
foo = "lol"
bar = 42
)
[...]
}
Structs
Definieren
type Config struct {
path string
timeout int64
}
Objekt des Typs Config erstellen
c := Config{path:"/foo/bar", timeout: 256}
Abrufen
log.Println(c.path)
Fun Fact: Konstaten können wesentlich mehr Nachkommastellen enthalten als Variablen
Fehlerbehandlung
Die meisten Funktionen geben auch einen err
zurück beim Aufruf
file, err := os.Open("/file/that/does/not/exist.txt")
if err != nil {
fmt.Printf("%v\n", err)
}
nil
ist dabei ein eingebauter Datentyp speziell für Fehler in Go.
fmt
Mal ein paar Notizen zu fmt
Um eine einzelne Zeile auszugeben, wie echo
in der Normalbenutzung
fmt.Println("foo")
Wenn ich noch den Filedescriptor angeben will, muss ich eine andere Funktion benutzen
fmt.Fprintln(os.Stderr, "foo")
Und wenn ich diverse Formatzeichen nutzen will, ebenso.
fmt.Printf("%s %d", lol, foo)
Formatstrings (wird noch fortgeführt)
%.2f float64 with 2 zeros
%s string
%i int
%v interface
%T show type of var
bufio
bufio
ist eine Standard Library mit sich unter anderen Files lesen lassen.
Buffered IO halt.
fhandle, err := os.Open("/path/to/file")
input := bufio.NewScanner(fhandle)
Um den Input dann zu verarbeiten
for input.Scan() {
fmt.Println(input.Text())
}
Buffer zu String Konvertieren
// response.Body = io.ReadCloser
response, _ := http.Get("https://golangcode.com/")
buf := new(bytes.Buffer)
buf.ReadFrom(response.Body)
newStr := buf.String()
os
OS hat ein paar Toole Bindings
Eine Datei oeffnen und ein Filehandle zurück bekommen
os.Open("file")
Von Stdin lesen
os.Stdin
os
beherrscht auch Argumente des Programms
os.Args[1]
strconv
Einen string
zum int
braucht einen Fehlerfall (Input: aaa?)
port, err := strconv.Atoi("8080")
flag
Flag ist sozusagen das os.Args
auf Steroiden. Der eingebaute
Argumentenparser kann auch Flags von Argumenten unterscheiden
var n = flag.Bool("n", false, "no newline")
func main() {
flag.Parse()
fmt.Print(strings.Join(flag.Args(), " "))
if !*n {
fmt.Println()
}
}
Logging
Besser als einfach nur fmt.Println mit
import log
log.Fatal("Process crashed and burned. Error: ", err)
Command Execution
Natürlich kann ich auch einfach Shell Commands wrappen in Go.
// execute commands
cmd := exec.Command(command, args...)
cmd := exec.Command("ls", "-lah", "-r")
output, err := cmd.CombinedOutput()
// check for errors
if err != nil {
log.Fatal("rvo crashed and burned.")
}
new
new()
ist eine Builtin Funktion
Um einen int
ohne Namen zu erzeugen und aus einer Funktion
zurück zu geben.
new()
So bewirkt
func newInt() *int {
return new(int)
}
das Gleiche wie
func newInt() *int {
var dummy int
return &dummy
}
Aber ist schöner und kürzer, weil man sich keinen Namen ausdenken muss der unnötig wäre.
Testing
Go hat eine eigene Test Suite dabei. Dafür muss man nur ein File
xxx_test.go
in das gleiche Verzeichnis legen und eine Funktion mit
TestXxx
benennen. Beispiel:
import testing
// Simple substract Test
func TestSubInt(t *testing.T) {
var want uint32 = 20
if got := subInt(60, 40); got != want {
t.Errorf("%d, want %d", got, want)
}
go test -v
Coverage
Auch Coverage kann man gleich im eingebauten Tool abfrühstücken:
> go test -cover -v
=== RUN TestBrighten
--- PASS: TestBrighten (0.00s)
=== RUN TestDarken
--- PASS: TestDarken (0.00s)
=== RUN TestIso
TestIso: nept_test.go:122: 20655, want 44247>20000
TestIso: nept_test.go:122: 37364, want 10>32777
--- FAIL: TestIso (0.00s)
=== RUN TestFlatten
--- PASS: TestFlatten (0.00s)
FAIL
coverage: 34.9% of statements
Go Routines
Man kann sehr einfach Go Routines einbauen die für das bestehende Programm Paralellität mit Threads einbaut.
import (
"sync"
)
func main() {
var wg sync.WaitGroup
for a := 0; a < 10; a++ {
wg.Add(1)
go edit(x, y)
}
wg.Wait()
}
func edit() {
defer wg.Done()
fmt.Println(x+y)
time.sleep(1)
}
json
// convert to bytes and read json
bytes := []byte(output)
var documents []Document
json.Unmarshal(bytes, &documents)
log.Printf("Entries found: %d", len(documents))
yaml
Beispiel YAML
challenges:
- Och ja... ich weiss auch nicht
- "Test"
- Ich weiss nicht
Und zum Ausgeben der Daten:
package main
import (
"fmt"
"log"
"io/ioutil" "gopkg.in/yaml.v2"
)
type Challenges struct {
Challenge []string `challenges`
}
func (c *Challenges) getConf() *Challenges{
yamlFile, _ := ioutil.ReadFile("challenges.yml")
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
log.Fatalf("Unmarshal: %v", err)
}
return c
}
func main() {
var c Challenges
c.getConf()
for _, x := range c.Challenge{
fmt.Println(x)
}
}
Das Struct
Challenges bekommt also eine Methode getConf()
zum Laden und
gibt ein befülltes Struct
zurück.