Menu
Coddy logo textTech

Panic, Defer und Recover

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 57 von 107.

Während Go die Rückgabe von Fehlern betont, sind manche Situationen tatsächlich nicht behebbar. Für diese Fälle bietet Go panic, defer und recover: einen Mechanismus zur Behandlung außergewöhnlicher Umstände.

panic beendet die normale Ausführung sofort. Es ist für Programmierfehler wie den Zugriff auf einen Index außerhalb des gültigen Bereichs reserviert, nicht für erwartete Fehler wie eine ungültige Benutzereingabe:

func MustGetConfig(key string) string {
    value, exists := config[key]
    if !exists {
        panic("missing required config: " + key)
    }
    return value
}

defer plant die Ausführung einer Funktion, wenn die umgebende Funktion zurückkehrt, unabhängig davon, ob dies normal oder aufgrund einer Panik geschieht. Zurückgestellte Aufrufe werden in umgekehrter Reihenfolge ausgeführt (zuletzt hinein, zuerst heraus):

func ProcessFile() {
    fmt.Println("Opening file")
    defer fmt.Println("Closing file")
    fmt.Println("Processing...")
    // Ausgabe: Opening file, Processing..., Closing file
}

recover fängt eine Panik ab und setzt die normale Ausführung fort. Es funktioniert nur innerhalb einer zurückgestellten Funktion:

func SafeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

Dieses Muster wandelt eine Panik in einen regulären Fehler um, sodass dein Programm fortfahren kann. Verwende es sparsam: Gib für erwartete Fehlerfälle vorzugsweise Fehler zurück und reserviere panic/recover für wirklich außergewöhnliche Situationen wie einen beschädigten Zustand oder nicht behebbare Bedingungen.

challenge icon

Aufgabe

Einfach

Erstellen wir einen sicheren Divisionsrechner, der zeigt, wie man panic, defer und recover verwendet, um außergewöhnliche Situationen ordnungsgemäß zu behandeln. Dein Rechner wird riskante Operationen ausführen und Panics in reguläre Fehler umwandeln, die normal behandelt werden können.

Du organisierst deinen Code über zwei Dateien:

  • calculator.go: Erstelle die Divisionslogik mit Panic-Wiederherstellung.

    Implementiere eine Divide-Funktion, die zwei Ganzzahlen entgegennimmt und mit der Nachricht "division by zero" eine Panic auslöst, wenn der Divisor null ist. Andernfalls gibt sie das ganzzahlige Ergebnis der Division zurück.

    Implementiere eine SafeDivide-Funktion, die zwei Ganzzahlen entgegennimmt und sowohl ein ganzzahliges Ergebnis als auch einen Fehler zurückgibt. Diese Funktion sollte:

    • defer mit einer anonymen Funktion verwenden, um jede Panic wiederherzustellen
    • Eine wiederhergestellte Panic in einen Fehler mit dem Format "calculation error: [panic message]" umwandeln
    • Intern die Funktion Divide aufrufen
    • Bei Erfolg das Ergebnis und nil zurückgeben oder bei einer wiederhergestellten Panic null und den Fehler zurückgeben
  • main.go: Lies zwei Ganzzahlen aus der Eingabe und verwende SafeDivide, um die Berechnung sicher durchzuführen. Zeige außerdem defer, indem du Bereinigungsnachrichten in der richtigen Reihenfolge ausgibst.

    Deine main-Funktion sollte:

    • Zu Beginn Starting calculation ausgeben
    • defer verwenden, um die Ausgabe von Cleanup complete einzuplanen
    • Ein weiteres defer verwenden, um die Ausgabe von Releasing resources einzuplanen
    • SafeDivide mit den Eingabewerten aufrufen
    • Je nach Ergebnis entweder Result: [value] oder Error: [error message] ausgeben

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Erste Ganzzahl (Dividend)
  • Zeile 2: Zweite Ganzzahl (Divisor)

Denke daran, dass zurückgestellte Aufrufe in umgekehrter Reihenfolge ausgeführt werden (zuletzt eingeplant, zuerst ausgeführt). Daher sollten deine Bereinigungsnachrichten in der entgegengesetzten Reihenfolge zu ihrer Einplanung erscheinen.

Wenn beispielsweise 20 und 4 gegeben sind, sollte deine Ausgabe wie folgt aussehen:

Starting calculation
Result: 5
Releasing resources
Cleanup complete

Und wenn 10 und 0 gegeben sind, sollte deine Ausgabe wie folgt aussehen:

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete

Probier es selbst

package main

import (
	"fmt"
)

func main() {
	// Eingabe lesen
	var dividend, divisor int
	fmt.Scanln(&dividend)
	fmt.Scanln(&divisor)

	// Startnachricht ausgeben
	fmt.Println("Starting calculation")

	// TODO: Use defer to schedule "Cleanup complete" message

	// TODO: Use defer to schedule "Releasing resources" message
	// Denk daran: deferred Aufrufe werden in LIFO-Reihenfolge ausgeführt (last-in, first-out)

	// TODO: Rufe SafeDivide mit den Eingabewerten auf

	// TODO: Gib entweder "Result: [value]" oder "Error: [error message]" aus
	// basierend darauf, ob ein Fehler zurückgegeben wurde
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Go-Compiler