Поиск…


Вступление

В Go непредвиденные ситуации обрабатываются с использованием ошибок , а не исключений. Этот подход более похож на подход C, используя errno, чем на Java или другие объектно-ориентированные языки с их блоками try / catch. Однако ошибка не целое, а интерфейс.

Функция, которая может терпеть неудачу, обычно возвращает ошибку в качестве ее последнего возвращаемого значения. Если эта ошибка не равна нулю , что-то пошло не так, и вызывающая функция должна предпринять соответствующие действия.

замечания

Обратите внимание, как в Go вы не вызываете ошибку. Вместо этого вы возвращаете ошибку в случае сбоя.

Если функция может выйти из строя, последнее возвращаемое значение обычно является типом 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) {
}

Создание значения ошибки

Самый простой способ создать ошибку - использовать пакет errors .

errors.New("this is an error")

Если вы хотите добавить дополнительную информацию об ошибке, пакет fmt также предоставляет полезный метод создания ошибок:

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

Вот полный пример, где ошибка возвращается из функции:

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")
}

Открыть в игровой площадке

Создание настраиваемого типа ошибки

В Go ошибка представляется любым значением, которое может описывать себя как строку. Любой тип, реализующий встроенный интерфейс error является ошибкой.

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

В следующем примере показано, как определить новый тип ошибки, используя строковый составной литерал.

// 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)
}

Теперь я могу использовать свой собственный тип ошибки как ошибку:

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
}

Возврат ошибки

В Go вы не вызываете ошибку. Вместо этого вы возвращаете error в случае сбоя.

// 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
}

Если метод возвращает несколько значений (и выполнение может завершиться с ошибкой), стандартное соглашение должно вернуть ошибку в качестве последнего аргумента.

// 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)
}

Обработка ошибки

Ошибки Go могут быть возвращены из вызова функции. Соглашение состоит в том, что если метод может завершиться неудачно, последний возвращаемый аргумент является error .

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

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

Вы используете несколько назначений переменных, чтобы проверить, не сработал ли этот метод.

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

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

Если вас не интересует ошибка, вы можете просто проигнорировать ее, назначив ее _ .

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

Конечно, игнорирование ошибки может иметь серьезные последствия. Поэтому это обычно не рекомендуется.

Если у вас есть несколько вызовов методов, и один или несколько методов в цепочке могут вернуть ошибку, вы должны распространять ошибку на первый уровень, который может ее обработать.

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
}

Восстановление от паники

Общей ошибкой является объявление среза и начало запроса индексов из него без его инициализации, что приводит к панике «index out of range». Следующий код объясняет, как восстановить панику без выхода из программы, что является нормальным поведением для паники. В большинстве ситуаций возврат ошибки таким способом, а не выход из программы при панике, полезен только для целей разработки или тестирования.

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)
        }
    }
}

Использование отдельной функции (а не закрытия) позволяет повторно использовать одну и ту же функцию в других функциях, подверженных панике.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow