Go Academy · Lekcja

panic, recover i defer

Kiedy wywoływać panic i jak bezpiecznie odzyskiwać działanie

Lekcja 4 z 412 kroki

panic, recover i defer to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Go Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Go Academy zawiera 4 lekcji w sumie.

Czym jest panic?

panic przerywa normalne wykonywanie bieżącej goroutine, rozwija stos wywołań, uruchamiając odroczone funkcje, i powoduje awarię programu, jeśli nie zostanie obsłużone:

package main
import "fmt"

func mustPositive(n int) int {
    if n <= 0 {
        panic(fmt.Sprintf("mustPositive: got %d", n))
    }
    return n
}

func main() {
    fmt.Println(mustPositive(5))  // 5
    // fmt.Println(mustPositive(-1)) // panics!
}

Kiedy używać panic

Używaj panic tylko w przypadku:

  • Naprawdę niemożliwych do odzyskania błędów programisty (naruszenia niezmienników)
  • Niemożliwych warunków wskazujących na błąd
  • Niepowodzeń podczas uruchamiania (brak konfiguracji, nieudane połączenia podczas inicjalizacji)

Nigdy nie używaj panic w przypadku oczekiwanych niepowodzeń — użyj zamiast tego error.

Podstawy defer

defer odracza wywołanie funkcji do momentu zakończenia funkcji otaczającej, niezależnie od sposobu jej zakończenia (normalnego, przez błąd lub panikę):

package main
import "fmt"

func main() {
    defer fmt.Println("third")
    defer fmt.Println("second")  // LIFO order
    defer fmt.Println("first")
    fmt.Println("running...")
    // Output:
    // running...
    // first
    // second
    // third
}

defer do sprzątania zasobów

Najczęściej defer służy do zapewnienia zwolnienia zasobów:

package main
import ("fmt"; "os")

func readFile(path string) (string, error) {
    f, err := os.Open(path)
    if err != nil {
        return "", fmt.Errorf("open: %w", err)
    }
    defer f.Close()  // always runs, even if we return early

    buf := make([]byte, 1024)
    n, _ := f.Read(buf)
    return string(buf[:n]), nil
}

recover — przechwytywanie panik

recover zatrzymuje panikę i zwraca jej wartość. Musi zostać wywołane bezpośrednio wewnątrz funkcji defer:

package main
import "fmt"

func safeDiv(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    return a / b, nil  // panics if b==0
}

func main() {
    r, err := safeDiv(10, 0)
    fmt.Println(r, err)
    // 0 recovered from panic: runtime error: integer divide by zero
}

recover musi być wywołane w odroczonej funkcji

recover działa tylko wtedy, gdy zostanie wywołane bezpośrednio wewnątrz odroczonej funkcji. Wywołanie go w innym miejscu zwraca nil:

package main
import "fmt"

func wrong() {
    // recover() here does nothing — not in defer
}

func correct() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("caught:", r)
        }
    }()
    panic("oops")
}

func main() { correct() }

Panika w kodzie biblioteki

Kod biblioteki niemal nigdy nie powinien pozwalać, aby paniki wydostały się do kodu wywołującego. Na granicy zamieniaj paniki na błędy:

package main
import ("fmt"; "runtime/debug")

func safeCall(fn func()) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
        }
    }()
    fn()
    return nil
}

func main() {
    err := safeCall(func() { panic("boom") })
    fmt.Println(err != nil) // true
}

Kolejność LIFO defer i pułapka w pętli

Odroczone wywołania wykonują się w kolejności ostatni weszło, pierwsze wyszło (LIFO). Uważaj na odraczanie wywołań wewnątrz pętli — użyj domknięcia lub funkcji pomocniczej:

package main
import "fmt"

func processFiles(names []string) {
    for _, name := range names {
        name := name  // capture loop variable
        defer func() { fmt.Println("close:", name) }()
    }
}

func main() {
    processFiles([]string{"a.txt", "b.txt", "c.txt"})
    // Output (LIFO): close: c.txt, close: b.txt, close: a.txt
}

defer z nazwanymi wartościami zwracanymi

Odroczone funkcje mogą odczytywać i modyfikować nazwane wartości zwracane — jest to przydatne do dodawania informacji do błędów:

package main
import "fmt"

func doWork() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("doWork: %w", err)
        }
    }()
    return fmt.Errorf("something failed")
}

func main() {
    err := doWork()
    fmt.Println(err) // doWork: something failed
}

panic a error: przewodnik po wyborze

Wybór między panic a error:

  • Używaj error: oczekiwane niepowodzenia (nie znaleziono pliku, przekroczenie limitu czasu sieci, nieprawidłowe dane wejściowe)
  • Używaj panic: błędy programistyczne (dostęp do nil mapy, wyjście poza zakres), niemożliwe stany, błędy inicjalizacji
  • Nigdy nie używaj panic na granicach API — kod wywołujący nie może go obsłużyć
  • Przechwytuj paniki na najwyższym poziomie goroutine w serwerach, aby uniknąć awarii

Szybkie sprawdzenie

Gdzie należy wywołać recover(), aby przechwycić panikę?

Podsumowanie: panic, recover, defer

Podsumowanie:

  • panic: odwija stos, wykonuje odroczone wywołania i powoduje awarię, jeśli nie zostanie przechwycony
  • defer: wykonuje się przy wyjściu z funkcji (normalnym, po błędzie lub panice); kolejność LIFO
  • recover: zatrzymuje panikę i zwraca jej wartość; musi znajdować się w defer
  • Paniki należy stosować w przypadku błędów programisty, a nie oczekiwanych niepowodzeń
  • Na granicach API należy zamieniać paniki na błędy
Bezpłatny start

Ucz się Go dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
51
Lekcje
203

Często zadawane pytania

Czy lekcja „panic, recover i defer” jest bezpłatna?

Tak — pełny tekst „panic, recover i defer” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Go Academy, przejdź na CoddyKit PRO. Kurs Go Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „panic, recover i defer”?

Kiedy wywoływać panic i jak bezpiecznie odzyskiwać działanie Ćwiczysz Go Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Go Academy?

Nie wymagamy żadnego doświadczenia. Go Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „panic, recover i defer”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Go Academy?

Tak. Każda lekcja Go Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Interfejs error
  2. Tworzenie własnych błędów
  3. Opakowywanie i rozpakowywanie błędów
  4. panic, recover i defer
← Powrót do Go Academy