Łapanie paniki w Golang

W poniższym kodzie, jeśli nie podano argumentu file, w linii 9 panic: runtime error: index out of range zostanie wywołana panika zgodnie z oczekiwaniami.

Jak mogę "złapać" tę panikę i poradzić sobie z nią, kiedy bezpośrednio przekazując do niej coś (os.Args[1]), co powoduje panikę? Podobnie jak try / catch w PHP lub try / except w Pythonie.

Szukałem tutaj na StackOverflow, ale nie znalazłem niczego, co by na to odpowiedziało.

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }
    fmt.Printf("%s", file)
}
Author: Luke B, 2014-07-29

7 answers

Go nie jest Pythonem, zanim go użyjesz, powinieneś dokładnie sprawdzić czy nie ma args:

func main() {
    if len(os.Args) != 2 {
         fmt.Printf("usage: %s [filename]\n", os.Args[0])
         os.Exit(1)
    }
    file, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", file)
}
 44
Author: OneOfOne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-07 16:42:05

Program Może odzyskać za pomocą wbudowanej funkcji recover():

Funkcja recover pozwala programowi zarządzać zachowaniem Załóżmy, że funkcja G odkłada funkcję D, która wywołuje recover, a panika zachodzi w funkcji na tym samym goroutine, w której G jest wykonywana. Gdy uruchamianie funkcji odroczonych osiągnie wartość D, wartość zwracana wywołania D do recover będzie wartością przekazaną wywołaniu panic. If D zwraca normalnie, bez uruchamiania nowego panic, Sekwencja paniki zatrzymuje się. W takim przypadku, stan funkcji wywołanych pomiędzy G i wywołaniem panic jest odrzucany, a normalne wykonanie wznawia się. Wszystkie funkcje odroczone przez G przed D są następnie uruchamiane, a wykonywanie G kończy się przez powrót do wywołującego.

Wartość zwracana recover jest zerowa, jeśli spełniony jest którykolwiek z poniższych warunków:

  • panic's argument was nil;
  • goroutine nie jest panika;
  • recover nie został wywołany bezpośrednio przez funkcję odroczoną.
Oto przykład jak tego użyć:
// access buf[i] and return an error if that fails.
func PanicExample(buf []int, i int) (x int, err error) {
    defer func() {
        // recover from panic if one occured. Set err to nil otherwise.
        if (recover() != nil) {
            err = errors.New("array index out of bounds")
        }
    }()

    x = buf[i]
}

Zauważ, że częściej niż nie, panika nie jest dobrym rozwiązaniem. Paradygmat Go polega na jawnym sprawdzaniu błędów. Program powinien wpadać w panikę tylko wtedy, gdy okoliczności, w jakich panikuje, nie zdarzają się podczas zwykłego wykonywania programu. Na przykład niemożność otwarcia pliku jest czymś, co może się zdarzyć i nie powinno wywołanie paniki, gdy zabraknie pamięci, jest warte paniki. Niemniej jednak mechanizm ten istnieje po to, aby móc łapać nawet te przypadki i być może zamykać je z wdziękiem.

 112
Author: fuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 00:04:11

Po pierwsze: nie chciałbyś tego robić. Obsługa błędów w stylu try-catch nie jest obsługą błędów. W Go można sprawdzić len(os.Args) pierwszy i dostęp do elementu 1 tylko wtedy, gdy jest obecny.

W rzadkich przypadkach musisz złapać panikę (a twoja sprawa jest, a nie jednym z nich!) stosować defer w połączeniu z recover. Zobacz http://golang.org/doc/effective_go.html#recover

 15
Author: Volker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-29 21:49:41

Niektóre oficjalne pakiety Golang używają panic/defer+recover jako throw/catch , ale tylko wtedy, gdy wymagają rozwinięcia dużego stosu połączeń. W pakietu JSON Golanga Użyciepanic/defer+recover jakothrow/catch jest najbardziej eleganckim rozwiązaniem.

Z http://blog.golang.org/defer-panic-and-recover

Prawdziwy przykład panic and recover można znaleźć w pakiecie json z biblioteki standardowej Go. Dekoduje Dane zakodowane w JSON z zestawem funkcji rekurencyjnych. W przypadku napotkania wadliwego JSON, parser wywołuje panic, aby rozwinąć stos do najwyższego poziomu wywołania funkcji, która odzyskuje się z panic i zwraca odpowiednią wartość błędu (patrz metody ' error 'I' unmarshal ' typu decodeState w decode.go).

Szukaj d.error( at http://golang.org/src/encoding/json/decode.go

W twoim przykładzie rozwiązaniem "idiomatycznym" jest sprawdzenie parametry przed ich użyciem, jak wskazywały inne rozwiązania.

Ale, jeśli chcesz / musisz złapać cokolwiek możesz zrobić:

package main

import (
    "fmt"
    "os"
)

func main() {

    defer func() { //catch or finally
        if err := recover(); err != nil { //catch
            fmt.Fprintf(os.Stderr, "Exception: %v\n", err)
            os.Exit(1)
        }
    }()

    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }

    fmt.Printf("%s", file)
}
 15
Author: Lucio M. Tato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-03 15:05:51

Możemy zarządzać paniką bez zatrzymywania procesu za pomocą recover. Wywołanie recover w dowolnej funkcji za pomocą defer zwróci wykonanie do wywołania funkcji. Recover zwraca dwie wartości, jedna to wartość logiczna, a druga to interfejs do odzyskania. Używając twierdzenia typu możemy uzyskać podstawową wartość błędu Możesz również wydrukować błąd podstawowy za pomocą recover.

defer func() {
    if r := recover(); r != nil {
        var ok bool
        err, ok = r.(error)
        if !ok {
            err = fmt.Errorf("pkg: %v", r)
        }
    }
}()
 4
Author: Himanshu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-18 10:16:15

Musiałem złapać panikę w walizce testowej. Zostałem przekierowany tutaj.

Func.go

var errUnexpectedClose = errors.New("Unexpected Close")
func closeTransaction(a bool) {
    if a == true {
        panic(errUnexpectedClose)
    }
}

Func_test.go

func TestExpectedPanic() {
    got := panicValue(func() { closeTransaction(true) })
    a, ok := got.(error)
    if a != errUnexpectedClose || !ok {
        t.Error("Expected ", errUnexpectedClose.Error())
    }
}

func panicValue(fn func()) (recovered interface{}) {
    defer func() {
        recovered = recover()
    }()
    fn()
    return
}

Używane od https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 (ref od VonC)

 3
Author: Sairam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-13 09:50:59

Zauważ, że odzyskiwanie leczenia błędu panic Execution (takiego jak próba indeksowania tablicy wyzwalacza poza granicami) może się zmienić z go 1.7 po issue 14965

Zobacz CL 21214 i jego test :

Runtime: make execution error]}

Make execution panics implementation Error as mandated by Run-time panics (specs) , zamiast panics with strings.

Kiedy Odzyskaj błąd paniki, możesz to zrobić:

if _, ok := recovered.(runtime.Error); !ok {

To jest nadal oceniane, i jako Dave Cheney . wzmianki:

Nie wiem co ludzie obecnie robią, ale z mojego POV to zostało złamane przez długi czas i nikt nie narzekał, więc albo wyraźnie polegają na zepsutym zachowaniu, albo nikogo to nie obchodzi. Tak czy siak myślę, że to dobry pomysł, aby uniknąć tej zmiany.

 1
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55