Kurz: import “fmt”
Teil des Kurses Go
package main
import "fmt"
func addiere(a, b int) int { return a + b }
// Mehrere Rückgabewerte
func teile(a, b int) (int, int) { return a / b, a % b }
// Benannte Rückgabewerte
func minMax(zahlen []int) (min, max int) {
min, max = zahlen[0], zahlen[0]
for _, z := range zahlen[1:] {
if z < min {
min = z
}
if z > max {
max = z
}
}
return // gibt min und max zurück
}
// Variable Anzahl Argumente
func summe(zahlen ...int) int {
s := 0
for _, z := range zahlen {
s += z
}
return s
}
func main() {
fmt.Println(addiere(3, 4))
q, r := teile(17, 5)
fmt.Println(q, r)
lo, hi := minMax([]int{5, 3, 9, 1})
fmt.Println(lo, hi)
fmt.Println(summe(), summe(1, 2, 3), summe([]int{4, 5}...))
}Ausgabe:
7
3 2
1 9
0 6 9Funktionen sind Werte
package main
import "fmt"
func wende(f func(int) int, x int) int { return f(x) }
func zaehlerErstellen() func() int { // Closure
n := 0
return func() int {
n++
return n
}
}
func main() {
quadrat := func(x int) int { return x * x }
fmt.Println(wende(quadrat, 7), wende(func(x int) int { return x + 1 }, 7))
z := zaehlerErstellen()
fmt.Println(z(), z(), z())
func() { fmt.Println("sofort aufgerufen") }()
}Ausgabe:
49 8
1 2 3
sofort aufgerufenFehler sind Werte
Go hat keine Ausnahmen. Funktionen, die scheitern können, geben als letzten Rückgabewert einen error zurück. Der Aufrufer prüft ihn sofort:
package main
import (
"errors"
"fmt"
"strconv"
)
var ErrNegativ = errors.New("negative Zahl") // Sentinel-Fehler
func wurzelInt(s string) (int, error) {
n, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("wurzelInt: ungültige Eingabe %q: %w", s, err) // %w: Ursache einpacken
}
if n < 0 {
return 0, ErrNegativ
}
r := 0
for (r+1)*(r+1) <= n {
r++
}
return r, nil
}
func main() {
for _, eingabe := range []string{"49", "-4", "abc"} {
r, err := wurzelInt(eingabe)
if err != nil {
fmt.Println("Fehler:", err)
if errors.Is(err, ErrNegativ) {
fmt.Println(" -> war negativ")
}
var numErr *strconv.NumError
if errors.As(err, &numErr) {
fmt.Println(" -> Zahlenfehler bei", numErr.Num)
}
continue
}
fmt.Println("Wurzel:", r)
}
}Ausgabe:
Wurzel: 7
Fehler: negative Zahl
-> war negativ
Fehler: wurzelInt: ungültige Eingabe "abc": strconv.Atoi: parsing "abc": invalid syntax
-> Zahlenfehler bei abcDas Muster if err != nil { return ..., err } ist allgegenwärtig in Go-Code. Es ist ausführlich, aber der Fehlerfluss ist immer sichtbar.
| Werkzeug | Zweck |
|---|---|
errors.New("text") | einfachen Fehler erzeugen |
fmt.Errorf("... %w", err) | Fehler mit Kontext einpacken |
errors.Is(err, Ziel) | prüft, ob Ziel in der Kette steckt |
errors.As(err, &typ) | holt einen Fehler eines bestimmten Typs aus der Kette |
eigener Typ mit Error() string | Fehler mit zusätzlichen Daten |
package main
import (
"errors"
"fmt"
)
type GuthabenFehler struct {
Fehlbetrag float64
}
func (e *GuthabenFehler) Error() string {
return fmt.Sprintf("zu wenig Guthaben, es fehlen %.2f", e.Fehlbetrag)
}
func abheben(stand, betrag float64) (float64, error) {
if betrag > stand {
return stand, &GuthabenFehler{betrag - stand}
}
return stand - betrag, nil
}
func main() {
if _, err := abheben(100, 130); err != nil {
var gf *GuthabenFehler
if errors.As(err, &gf) {
fmt.Println(err, "|", gf.Fehlbetrag)
}
}
}Ausgabe:
zu wenig Guthaben, es fehlen 30.00 | 30defer, panic und recover
panic ist für Programmierfehler gedacht (nicht für normale Fehler): Es beendet das Programm, führt vorher aber alle defer-Aufrufe aus. recover fängt eine Panic innerhalb eines defer:
package main
import "fmt"
func sicher(f func()) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("abgefangen: %v", r)
}
}()
f()
return nil
}
func main() {
fmt.Println(sicher(func() { fmt.Println("alles gut") }))
fmt.Println(sicher(func() {
var feld []int
_ = feld[5] // index out of range -> panic
}))
fmt.Println(sicher(func() { panic("selbst ausgelöst") }))
}Ausgabe:
alles gut
<nil>
abgefangen: runtime error: index out of range [5] with length 0
abgefangen: selbst ausgelöstNutze panic nur bei nicht behebbaren Zuständen (zum Beispiel beim Start, wenn eine Pflicht-Konfiguration fehlt). Bibliotheken sollen Fehler zurückgeben.
Merke
- Mehrere Rückgabewerte; der letzte ist bei fehlbaren Funktionen ein
error - Fehler sind normale Werte:
if err != nil { return ..., err } fmt.Errorf("... %w", err)packt Kontext ein;errors.Is/errors.Aswerten aus- Funktionen sind Werte und können Closures sein
panicundrecovernur für Ausnahmesituationen
Übungsaufgabe
Schreibe teile(a, b float64) (float64, error), die bei b == 0 einen eigenen Fehler liefert, und teste es mit mehreren Eingaben.
Quiz zur Selbstkontrolle
Wie melden Go-Funktionen Fehler?
- Sie geben einen error als letzten Rückgabewert zurück (richtig)
- Sie werfen Ausnahmen
- Sie rufen exit auf
- Sie geben null zurück
Was macht %w in fmt.Errorf?
- Packt den ursprünglichen Fehler ein, sodass errors.Is ihn findet (richtig)
- Gibt Warnungen aus
- Formatiert eine Zahl
- Schreibt in eine Datei
Wann nutzt man panic?
- Nur bei unbehebbaren Programmierfehlern (richtig)
- Für jeden Fehler
- Für Benutzereingaben
- Für Dateifehler
Weiter im Kurs
Zurück: Bedingungen, Schleifen und switch
Weiter: Arrays, Slices und Maps
Alle Kapitel: Go im Überblick