Historia pytania:
Operatory defer, panic i recover to ważne mechanizmy zarządzania przepływem wykonywania w Go. Operator defer jest używany do opóźnionego wykonania funkcji, panic inicjuje błąd (awaryjne zakończenie), a recover pozwala przechwycić awarię i kontynuować wykonanie.
Problem:
Bez odpowiedniego użycia tych narzędzi trudno jest poprawnie finalizować zasoby i wdrażać odporność na awarie. Nieprzewidywalne zakończenie działania funkcji, niezwolnione zasoby i niekontrolowane „wyjście” aplikacji przy błędach to częste problemy przy niewłaściwym podejściu.
Rozwiązanie:
Odpowiednie zastosowanie defer zapewnia bezpieczne zwolnienie zasobów, nawet w przypadku błędów. panic to mechanizm awaryjnego zakończenia w sytuacjach nadzwyczajnych, który powinien być używany tylko dla naprawdę wyjątkowych przypadków. recover daje możliwość „uratowania” wykonania wewnątrz opóźnionej funkcji.
Przykład kodu:
func riskyFunction() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in riskyFunction:", r) } }() fmt.Println("Doing some work...") panic("something bad happened!") } func main() { riskyFunction() fmt.Println("After riskyFunction") }
Kluczowe cechy:
defer jest zawsze wywoływane w odwrotnej kolejności przed wyjściem z funkcji. Nawet w przypadku panic.recover działa TYLKO wewnątrz opóźnionych funkcji.panic przerywa wykonanie bieżącej gorutyny; jeśli nie jest uchwycone przez recover — kończy proces.Dlaczego recover nie działa poza defer wewnątrz tej samej funkcji, w której wystąpił panic?
recover zwraca niezerową wartość (czyli przechwytuje panikę) tylko wtedy, gdy jest wywoływane z funkcji defer, zagnieżdżonej w tej samej funkcji, w której wystąpiła panika. Jeśli wywołasz recover bezpośrednio, zawsze zwraca nil.
Przykład kodu:
func f() { panic("fail!") r := recover() // nie działa! }
Czy można wywołać defer po panic i on zostanie wykonany?
Nie. Po panic wszystkie już zarejestrowane wywołania defer zostaną wykonane, ale nowe defer po panic nie będą wywołane, ponieważ wykonanie funkcji już się „zwija”.
Czy można odzyskać (recover) z panic, która wystąpiła w innej gorutynie?
Nie, recover działa tylko dla paniki w bieżącej gorutynie. Jeśli inna gorutyna panikuje i wywołanie recover nie miało miejsca w tej samej gorutynie — aplikacja zakończy działanie.
W projekcie wszystkie błędy były zgłaszane przez panic, a obsługa recovery miała miejsce tylko w funkcji głównej. Prowadziło to do tego, że zasoby nie były zamykane, część danych była tracona, a logi były nieczytelne.
Zalety:
Wady:
W każdym krytycznym miejscu używano defer do zwalniania zasobów, panic stosowano tylko w naprawdę wyjątkowych sytuacjach, a recover — do izolacji awarii w mniej krytycznych częściach. Przy tym logowano wszystkie szczegóły błędu.
Zalety:
Wady: