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.
Aufgabe
EinfachErstellen 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:defermit 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
Divideaufrufen - Bei Erfolg das Ergebnis und
nilzurückgeben oder bei einer wiederhergestellten Panic null und den Fehler zurückgeben
main.go: Lies zwei Ganzzahlen aus der Eingabe und verwendeSafeDivide, um die Berechnung sicher durchzuführen. Zeige außerdemdefer, indem du Bereinigungsnachrichten in der richtigen Reihenfolge ausgibst.Deine main-Funktion sollte:
- Zu Beginn
Starting calculationausgeben deferverwenden, um die Ausgabe vonCleanup completeeinzuplanen- Ein weiteres
deferverwenden, um die Ausgabe vonReleasing resourceseinzuplanen SafeDividemit den Eingabewerten aufrufen- Je nach Ergebnis entweder
Result: [value]oderError: [error message]ausgeben
- Zu Beginn
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 completeUnd wenn 10 und 0 gegeben sind, sollte deine Ausgabe wie folgt aussehen:
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup completeProbier es selbst
package main
import (
"fmt"
)
func main() {
// Eingabe lesen
var dividend, divisor int
fmt.Scanln(÷nd)
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
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler