11 Fehlerbehandlung

Fehlerbehandlung ist ein wichtiger Bestandteil der Softwareentwicklung, da sie uns hilft, Programme robuster und fehlertoleranter zu gestalten.

11.1 Fehler als Rückgabewerte

In Go werden Fehler in der Regel als Rückgabewerte von Funktionen behandelt. Dies ermöglicht eine explizite und einfache Fehlerprüfung. Hier ist ein grundlegendes Beispiel:

package main

import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(4, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

In diesem Beispiel:

  1. Die Funktion divide nimmt zwei Gleitkommazahlen a und b und gibt entweder das Ergebnis der Division oder einen Fehler zurück.
  2. Wenn b null ist, erzeugen wir einen neuen Fehler mit errors.New("division by zero") und geben ihn zurück.
  3. Im main-Programm rufen wir divide auf und überprüfen, ob ein Fehler aufgetreten ist. Wenn ja, drucken wir die Fehlermeldung aus. Andernfalls drucken wir das Ergebnis aus.

11.2 Die errors-Bibliothek

Go bietet das errors-Paket zur Erstellung und Verwaltung von Fehlern. Das Paket enthält die Funktion errors.New, mit der wir einfache Fehler erstellen können. Es gibt jedoch auch die Möglichkeit, eigene Fehlerstrukturen zu definieren.

11.2.1 Benutzerdefinierte Fehler

Hier ein Beispiel für die Erstellung eines benutzerdefinierten Fehlers:

package main

import (
    "fmt"
)

type MyError struct {
    Code    int
    Message string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

func doSomething() error {
    return &MyError{Code: 123, Message: "Something went wrong"}
}

func main() {
    err := doSomething()
    if err != nil {
        fmt.Println(err)
    }
}

In diesem Beispiel:

  1. Wir definieren eine MyError-Struktur mit den Feldern Code und Message.
  2. Die Methode Error implementiert die error-Schnittstelle und gibt eine formatierte Fehlermeldung zurück.
  3. Die Funktion doSomething gibt einen neuen MyError zurück.
  4. Im main-Programm rufen wir doSomething auf und drucken den Fehler aus, falls einer auftritt.

11.3 Fehlerverkettung

In komplexen Programmen ist es oft notwendig, Fehler weiterzugeben. Dies kann durch Fehlerverkettung erreicht werden.

11.3.1 Beispiel

Hier ein Beispiel für die Fehlerverkettung:

package main

import (
    "fmt"
    "os"
)

func readFile(filename string) (string, error) {
    file, err := os.Open(filename)
    if err != nil {
        return "", fmt.Errorf("failed to open file: %w", err)
    }
    defer file.Close()

    data := make([]byte, 100)
    _, err = file.Read(data)
    if err != nil {
        return "", fmt.Errorf("failed to read file: %w", err)
    }

    return string(data), nil
}

func main() {
    content, err := readFile("example.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("File content:", content)
}

In diesem Beispiel:

  1. Die Funktion readFile versucht, eine Datei zu öffnen und ihren Inhalt zu lesen. Bei jedem Fehler verwenden wir fmt.Errorf, um den Fehler zu umschließen und zusätzliche Kontextinformationen hinzuzufügen.
  2. Im main-Programm rufen wir readFile auf und drucken den Fehler aus, falls einer auftritt.

11.4 Panic und Recover

Neben der expliziten Fehlerbehandlung bietet Go auch Mechanismen für die Behandlung von Laufzeitfehlern durch panic und recover.

11.4.1 Verwendung von Panic und Recover

Hier ein Beispiel für die Verwendung von panic und recover:

package main

import "fmt"

func mightPanic() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    panic("something went wrong")
}

func main() {
    fmt.Println("Starting main")
    mightPanic()
    fmt.Println("Ending main")
}

In diesem Beispiel:

  1. Die Funktion mightPanic löst eine Panik mit panic("something went wrong") aus.
  2. Ein defer-Funktionsaufruf fängt die Panik mit recover ab und druckt eine Nachricht aus, wenn eine Panik erkannt wird.
  3. Im main-Programm rufen wir mightPanic auf. Der defer-Funktionsaufruf innerhalb von mightPanic sorgt dafür, dass das Programm nicht abstürzt, sondern die Panik abfängt und fortfährt.