Szukaj…


Wprowadzenie

W Go nieoczekiwane sytuacje są obsługiwane przy użyciu błędów , a nie wyjątków. To podejście jest bardziej podobne do podejścia C przy użyciu errno niż do języka Java lub innych języków zorientowanych obiektowo, z ich blokami try / catch. Jednak błąd nie jest liczbą całkowitą, ale interfejsem.

Funkcja, która może zawieść, zwykle zwraca błąd jako ostatnią zwracaną wartość. Jeśli ten błąd nie jest zerowy , coś poszło nie tak i wywołujący funkcję powinien podjąć odpowiednie działanie.

Uwagi

Zwróć uwagę, że w Go nie zgłaszasz błędu. Zamiast tego zwracasz błąd w przypadku awarii.

Jeśli funkcja może zawieść, ostatnia zwracana wartość jest zazwyczaj typem error .

// This method doesn't fail
func DoSomethingSafe() {
}

// This method can fail
func DoSomething() (error) {
}

// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
}

Tworzenie wartości błędu

Najprostszym sposobem na utworzenie błędu jest użycie pakietu errors .

errors.New("this is an error")

Jeśli chcesz dodać dodatkowe informacje do błędu, pakiet fmt zapewnia również przydatną metodę tworzenia błędów:

var f float64
fmt.Errorf("error with some additional information: %g", f)

Oto pełny przykład, w którym błąd jest zwracany z funkcji:

package main

import (
    "errors"
    "fmt"
)

var ErrThreeNotFound = errors.New("error 3 is not found")

func main() {
    fmt.Println(DoSomething(1)) // succeeds! returns nil
    fmt.Println(DoSomething(2)) // returns a specific error message
    fmt.Println(DoSomething(3)) // returns an error variable
    fmt.Println(DoSomething(4)) // returns a simple error message
}

func DoSomething(someID int) error {
    switch someID {
    case 3:
        return ErrThreeNotFound
    case 2:
        return fmt.Errorf("this is an error with extra info: %d", someID)
    case 1:
        return nil
    }

    return errors.New("this is an error")
}

Otwórz na placu zabaw

Tworzenie niestandardowego typu błędu

W Go błąd jest reprezentowany przez dowolną wartość, która może opisywać się jako ciąg znaków. Każdy typ, który implementuje wbudowany interfejs error , jest błędem.

// The error interface is represented by a single
// Error() method, that returns a string representation of the error
type error interface {
    Error() string
}

Poniższy przykład pokazuje, jak zdefiniować nowy typ błędu za pomocą literału złożonego ciągu.

// Define AuthorizationError as composite literal
type AuthorizationError string

// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
    return string(e)
}

Teraz mogę użyć mojego niestandardowego typu błędu jako błędu:

package main

import (
    "fmt"
)

// Define AuthorizationError as composite literal
type AuthorizationError string

// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
    return string(e)
}

func main() {
    fmt.Println(DoSomething(1)) // succeeds! returns nil
    fmt.Println(DoSomething(2)) // returns an error message
}

func DoSomething(someID int) error {
    if someID != 1 {
        return AuthorizationError("Action not allowed!")
    }

    // do something here

    // return a nil error if the execution succeeded
    return nil
}

Zwracanie błędu

W Go nie zgłaszasz błędu. Zamiast tego zwracasz error w przypadku awarii.

// This method can fail
func DoSomething() error {
    // functionThatReportsOK is a side-effecting function that reports its
    // state as a boolean. NOTE: this is not a good practice, so this example
    // turns the boolean value into an error. Normally, you'd rewrite this
    // function if it is under your control.
    if ok := functionThatReportsOK(); !ok {
        return errors.New("functionThatReportsSuccess returned a non-ok state")
    }

    // The method succeeded. You still have to return an error
    // to properly obey to the method signature.
    // But in this case you return a nil error.
    return nil
}

Jeśli metoda zwraca wiele wartości (a wykonanie może się nie powieść), standardową konwencją jest zwrócenie błędu jako ostatniego argumentu.

// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
    if os.Getenv("ERROR") == "1" {
        return "", errors.New("The method failed")
    }

    s := "Success!"

    // The method succeeded.
    return s, nil
}

result, err := DoAndReturnSomething()
if err != nil {
    panic(err)
}

Obsługa błędu

Błędy w Go mogą być zwracane z wywołania funkcji. Konwencja jest taka, że jeśli metoda może zawieść, ostatni zwrócony argument jest error .

func DoAndReturnSomething() (string, error) {
    if os.Getenv("ERROR") == "1" {
        return "", errors.New("The method failed")
    }

    // The method succeeded.
    return "Success!", nil
}

Używasz wielu przypisań zmiennych, aby sprawdzić, czy metoda się nie powiodła.

result, err := DoAndReturnSomething()
if err != nil {
    panic(err)
}

// This is executed only if the method didn't return an error
fmt.Println(result)

Jeśli nie jesteś zainteresowany błędem, możesz go po prostu zignorować, przypisując go do _ .

result, _ := DoAndReturnSomething()
fmt.Println(result)

Oczywiście ignorowanie błędu może mieć poważne konsekwencje. Dlatego generalnie nie jest to zalecane.

Jeśli masz wiele wywołań metod, a jedna lub więcej metod w łańcuchu może zwrócić błąd, należy propagować błąd na pierwszy poziom, który może go obsłużyć.

func Foo() error {
    return errors.New("I failed!")    
}

func Bar() (string, error) {
    err := Foo()
    if err != nil {
        return "", err
    }

    return "I succeeded", nil
}

func Baz() (string, string, error) {
    res, err := Bar()
    if err != nil {
        return "", "", err
    }

    return "Foo", "Bar", nil
}

Odzyskiwanie z paniki

Częstym błędem jest deklarowanie wycinka i rozpoczęcie od niego żądania indeksów bez inicjowania go, co prowadzi do paniki „indeks poza zakresem”. Poniższy kod wyjaśnia, jak odzyskać panikę bez wychodzenia z programu, co jest normalnym zachowaniem w przypadku paniki. W większości sytuacji zwracanie błędu w ten sposób zamiast wychodzenia z programu w panice jest przydatne tylko do celów programistycznych lub testowych.

type Foo struct {
    Is []int
}

func main() {
    fp := &Foo{}
    if err := fp.Panic(); err != nil {
        fmt.Printf("Error: %v", err)
    } 
    fmt.Println("ok")
}

func (fp *Foo) Panic() (err error) {
    defer PanicRecovery(&err)
    fp.Is[0] = 5
    return nil
}

func PanicRecovery(err *error) {

    if r := recover(); r != nil {
        if _, ok := r.(runtime.Error); ok {
             //fmt.Println("Panicing")
             //panic(r)
             *err = r.(error) 
        } else {
            *err = r.(error)
        }
    }
}

Zastosowanie oddzielnej funkcji (zamiast zamknięcia) pozwala na ponowne użycie tej samej funkcji w innych funkcjach podatnych na panikę.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow