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 9
Funktionen 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 aufgerufen
Fehler 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 abc
Das 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 | 30
defer, 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öst
Nutze 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
Aufgabe
Schreibe teile(a, b float64) (float64, error), die bei b == 0 einen eigenen Fehler liefert, und teste es mit mehreren Eingaben.