Funktionen & Multi-Return
Funktionen in Go: Parameter, Rueckgabewerte, multiple Returns, benannte Returns und Varianten mit ... Argumenten.
Inhaltsverzeichnis
Funktionen in Go
Funktionen sind einer der beliebtesten Teile der Sprache - besonders wegen mehrfacher Rueckgabewerte, die Goโs Fehlerbehandlung so klar machen.
Grundform
func gruesse(name string) {
fmt.Printf("Hallo, %s!\n", name)
}
func main() {
gruesse("Anna")
}
funcleitet eine Funktion eincamelCasefuer Funktionsnamen- Parameter brauchen immer einen Typ - nach dem Namen
Rueckgabewert
Der Rueckgabetyp steht nach den Parametern:
func addiere(a, b int) int {
return a + b
}
Wenn mehrere Parameter den gleichen Typ haben, muss der Typ nur beim letzten stehen: a, b int = a int, b int.
Mehrere Rueckgabewerte
Goโs Killer-Feature: Funktionen koennen mehrere Werte zurueckgeben. Die Typen stehen in Klammern:
func minMax(zahlen []int) (int, int) {
min, max := zahlen[0], zahlen[0]
for _, z := range zahlen {
if z < min { min = z }
if z > max { max = z }
}
return min, max
}
func main() {
min, max := minMax([]int{3, 1, 4, 1, 5, 9, 2, 6})
fmt.Println(min, max) // 1 9
}
Der Standard-Pattern: value, error
Die bei weitem haeufigste Form in Go:
func liesZahl(s string) (int, error) {
return strconv.Atoi(s)
}
func main() {
if zahl, err := liesZahl("42"); err == nil {
fmt.Println("Zahl:", zahl)
} else {
fmt.Println("Fehler:", err)
}
}
Diese value, err-Konvention taucht ueberall in Go-Code auf. Wenn du sie verinnerlichst, wirst du Go-Code sofort lesen koennen.
Benannte Rueckgabewerte
Du kannst Rueckgabewerte benennen. Sie werden am Anfang der Funktion mit ihrem Zero Value initialisiert, und du kannst ein nacktes return benutzen:
func teile(a, b float64) (ergebnis float64, err error) {
if b == 0 {
err = fmt.Errorf("Division durch Null")
return
}
ergebnis = a / b
return
}
Das ist praktisch bei mehreren Return-Pfaden - aber fuer kurze Funktionen lohnt es sich nicht. Halte deine Funktionen klein und verwende explizite Returns.
Variadic Parameter (...)
Funktionen koennen beliebig viele Argumente eines Typs nehmen:
func summe(zahlen ...int) int {
total := 0
for _, z := range zahlen {
total += z
}
return total
}
func main() {
fmt.Println(summe(1, 2, 3)) // 6
fmt.Println(summe(1, 2, 3, 4, 5)) // 15
zahlen := []int{10, 20, 30}
fmt.Println(summe(zahlen...)) // 60 - Slice "entpacken"
}
fmt.Println selbst ist eine variadic Funktion - deshalb kannst du beliebig viele Werte uebergeben.
Funktionen als Werte
Funktionen sind in Go First-Class Values:
func main() {
addiere := func(a, b int) int {
return a + b
}
fmt.Println(addiere(3, 4)) // 7
}
Du kannst sie an andere Funktionen uebergeben:
func anwenden(f func(int) int, wert int) int {
return f(wert)
}
func verdopple(n int) int { return n * 2 }
func main() {
fmt.Println(anwenden(verdopple, 5)) // 10
fmt.Println(anwenden(func(n int) int { return n * n }, 5)) // 25
}
Closures
Anonyme Funktionen โmerkenโ sich Variablen aus ihrem Scope:
func zaehler() func() int {
n := 0
return func() int {
n++
return n
}
}
func main() {
naechster := zaehler()
fmt.Println(naechster()) // 1
fmt.Println(naechster()) // 2
fmt.Println(naechster()) // 3
}
Jeder Aufruf von zaehler() erzeugt einen eigenen n-Wert - Closures sind kleine, private Zustandsmaschinen.
defer innerhalb von Funktionen
Wir haben defer kurz gesehen - in Funktionen wird es wichtig:
func schreibeProtokoll(datei string, eintrag string) error {
f, err := os.Create(datei)
if err != nil {
return err
}
defer f.Close() // wird **garantiert** aufgerufen, egal welches return
_, err = f.WriteString(eintrag)
return err
}
defer garantiert Cleanup - keine vergessene Close-Operation, auch bei Fehlern.
Panic und Recover (Ausnahmefaelle)
Go hat keine Exceptions - Fehler werden ueber error zurueckgegeben. Fuer wirklich aussergewoehnliche Situationen gibt es panic und recover:
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Erholung von:", r)
}
}()
panic("etwas ist schiefgelaufen")
}
Aber: Nutze das selten. Der Standard ist return err.
Kleine Stil-Regeln
- Eine Funktion sollte eine Aufgabe haben. Wenn sie nicht in einen Bildschirm passt, ueberlege Zerlegung.
- Exportieren (oeffentlich) = grosser Anfangsbuchstabe.
GroesseBerechnenist oeffentlich,groesseBerechnenprivat. - Kurze Variablen fuer kurze Scopes (
i,r,s), beschreibende Namen fuer groessere Scopes.
Zusammenfassung
func name(params) (returns) { ... }- Parameter brauchen einen Typ, Typ kommt nach dem Namen
- Mehrere Rueckgabewerte sind das Standardidiom (
value, err) - Variadic Parameter mit
...Typ - Funktionen sind Werte - du kannst sie uebergeben, speichern, in Closures fangen
defergarantiert Cleanup am Funktionsende
Im naechsten Kapitel: Slices und Maps - die wichtigsten Datenstrukturen.