Go
Обработка ошибок
Поиск…
Вступление
В 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)
}
}
}
Использование отдельной функции (а не закрытия) позволяет повторно использовать одну и ту же функцию в других функциях, подверженных панике.