cpp

Интерфейс error: описание ошибки

Очень часто ошибки возникают при вызове функции внутри нее. В этом случае в языке Go принято возвращать из функции объект ошибки типа error. Объект ошибки возвращает, например, функция Println():

fmt.Println(a ...interface{}) (n int, err error)

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

n, err := fmt.Println("Строка")
if err != nil {
   fmt.Fprintln(os.Stderr, err)
} else {
   fmt.Println("n =", n)
}

Тип error является интерфейсом, внутри которого объявлен метод Error(), возвращающий описание ошибки:

type error interface {
   Error() string
}

В своих программах вы можете создать пользовательскую структуру и реализовать интерфейс error (достаточно добавить определение метода Error()). Однако существует более простой способ создания объекта типа error — достаточно вызвать функцию Errorf() из пакета fmt. Формат функции:

fmt.Errorf(format string, a ...interface{}) error

В первом параметре функция принимает строку формата. Внутри строки формата можно указать обычные символы и спецификаторы формата, начинающиеся с символа % (см. описание функции Printf() в разд. 1.9). Вместо спецификаторов формата подставляются значения, указанные в качестве параметров. Количество спецификаторов должно совпадать с количеством переданных параметров. Пример:

err := fmt.Errorf("%s = %d", "x", 10)
fmt.Println(err)         // x = 10
fmt.Println(err.Error()) // x = 10

Создать объект можно также с помощью функции New() из пакета errors. Формат функции:

errors.New(text string) error

Пример:

// import "errors"
err := errors.New("Описание ошибки")
fmt.Println(err)         // Описание ошибки
fmt.Println(err.Error()) // Описание ошибки

В строке формата функции Errorf() можно указать спецификатор формата %w. Этому спецификатору нужно сопоставить объект предыдущей ошибки. Если спецификатор %w существует, то возвращаемый объект будет содержать метод Unwrap(). Получить значение, возвращаемое этим методом, позволяет функция Unwrap() из пакета errors. Формат функции:

errors.Unwrap(err error) error

Пример:

err1 := errors.New("Описание ошибки")
err2 := fmt.Errorf("%s = %d [%w]", "x", 10, err1)
fmt.Println(err2)                // x = 10 [Описание ошибки]
fmt.Println(errors.Unwrap(err2)) // Описание ошибки

В качестве примера вернем объект ошибки из функции разными способами (листинг 13.1).

Листинг 13.1. Интерфейс error

package main

import (
   "errors"
   "fmt"
)

type MyError struct {
   Msg string
   Err error
}

func main() {
   fmt.Println(test(10))           // 20 <nil>
   fmt.Println(test(-5))           // 0 Error: x < 0; x = -5
   fmt.Println(test2(10))          // 20 <nil>
   n, err := test2(-5)
   fmt.Println(n)                  // 0
   fmt.Println(err)                // Error: x < 0
   fmt.Println(errors.Unwrap(err)) // Описание ошибки
}
func (e *MyError) Error() string {
   return e.Msg
}
func (e *MyError) Unwrap() error {
   return e.Err
}
func test(x int) (int, error) {
   if x < 0 {
      return 0, fmt.Errorf("Error: x < 0; x = %d", x)
   }
   return x * 2, nil
}
func test2(x int) (int, error) {
   if x < 0 {
      return 0, &MyError{"Error: x < 0", errors.New("Описание ошибки")}
   }
   return x * 2, nil
}

Через переменную с типом error доступен только метод Error(). Однако мы можем реализовать интерфейс в своей структуре и вернуть объект с какой-либо дополнительной информацией. Чтобы добраться до этой информации нужно преобразовать объект типа error в объект типа структуры:

var err1 error = &MyError{"Описание ошибки", errors.New("Err")}
if err2, ok := err1.(*MyError); ok {
   fmt.Println(err2.Msg)      // Описание ошибки
   fmt.Println(err2.Err)      // Err
   fmt.Println(err2.Unwrap()) // Err
} else {
   fmt.Println(err1)
}

Выполнить аналогичную операцию позволяет функция As() из пакета errors. Формат функции:

errors.As(err error, target interface{}) bool

Если err может быть преобразован в тип структуры target, то выполняется преобразование и функция возвращает значение true. В противном случае функция возвращает false. В качестве параметра target указывается адрес переменной, в которую будет записан преобразованный объект. Пример:

var err1 error = &MyError{"Описание ошибки", errors.New("Err")}
var err2 *MyError
if errors.As(err1, &err2) {
   fmt.Println(err2.Msg)      // Описание ошибки
   fmt.Println(err2.Err)      // Err
   fmt.Println(err2.Unwrap()) // Err
} else {
   fmt.Println(err1)
}

Функция As() не напрямую выполняет преобразование. Она проверяет всю цепочку объектов ошибки. Цепочка состоит из собственно исходного объекта, а также из объектов, возвращаемых методами Unwrap(). В результате выполняется преобразование первого объекта из цепочки, совпадающего с типом структуры:

var e error = &MyError{Msg: "Описание ошибки"}
var err1 error = fmt.Errorf("[%w]", e)
var err2 *MyError
if errors.As(err1, &err2) {
   fmt.Println(err2.Msg)      // Описание ошибки
   fmt.Println(err2.Err)      // <nil>
   fmt.Println(err2.Unwrap()) // <nil>
} else {
   fmt.Println(err1)
}

Выполнить сравнение позволяет функция Is() из пакета errors. Формат функции:

errors.Is(err error, target error) bool

Функция возвращает значение true, если в цепочке объектов ошибок существует объект равный target. В противном случае возвращается значение false. Пример:

// import "io/fs"
var err error = fmt.Errorf("[%w]", fs.ErrExist)
if errors.Is(err, fs.ErrExist) {
   fmt.Println("err == fs.ErrExist") // err == fs.ErrExist
}

Если структура содержит метод Is(), то функция Is() будет вызывать этот метод:

func (e MyError) Is(target error) bool {
   return target == fs.ErrExist
}

Пример сравнения:

var err error = &MyError{Msg: "Описание ошибки"}
if errors.Is(err, fs.ErrExist) {
   fmt.Println("err == fs.ErrExist") // err == fs.ErrExist
}

Учебник Go (Golang)
Учебник Go (Golang) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

cpp