К вопросу обработки ошибок в Go

К вопросу обработки ошибок в Go

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

Давайте посмотрим на этот блок кода:

type myError struct{}

func (c *myError) Error() string {
	return "Unexpected Error."
}

func run() ([]byte, *myError) {
	return nil, nil
}

func main() {
	var err error
	if _, err = fail(); err != nil {
		log.Fatal("We are failed")
	}
	log.Println("Everything is OK")
}

Если мы запустим эту программу, то неожиданно получим ошибку "We are failed". Почему же это происходит? Это связано с тем, что функция run использует конкретную реализацию ошибки, а не интерфейс. В этом случае nil pointer типа myError хранится внутри переменной err. А это не то же самое, что пустое значение интерфейса типа error.

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

Одна из проблем заключается в том, что не все функции могут обрабатывать ошибку. Просто не все функции ведут журнал ошибок. Что происходит, когда ошибка передается обратно вверх по стеку вызовов и не может быть обработана функцией, получившей ее? Ошибка должна быть обернута в контекст, чтобы функция, которая в конечном итоге обработает ее, могла сделать это правильно.

Есть два варианта обертывания дополнительного контекста вокруг ошибки. Можно использовать сторонний пакет Dave Cheney's errors package или использовать поддержку стандартной библиотеки, которую можно найти в пакетах errors и fmt. Что бы я ни решил, важно аннотировать ошибки для достаточного контекста, чтобы помочь определить и устранить проблемы. Как во время выполнения, так и после.

Пример использования стороннего пакета:

package main
import (
	"fmt"
	"github.com/pkg/errors"
)

type MainError struct {
	State int
}
func (c *MainError) Error() string {
	return fmt.Sprintf("Main Error, State: %d", c.State)
}
func main() {
	if err := first(10); err != nil {
		switch v := errors.Cause(err).(type) {
		case *MainError:
			fmt.Println("Custom Main Error:", v.State)
		default:
			fmt.Println("Default Error")
		}
	}
	fmt.Printf("%v\n", err)
}
func first(i int) error {
	if err := second(i); err != nil {
		return errors.Wrapf(err, "secondCall(%d)", i)
	}
	return nil
}
func second(i int) error {
	return &MainError{99}
}

Вывод будет следующим:

 

Custom Main Error: 99

second(10): Main Error, State: 99

 

Что мне нравится в этом пакете, так это наличие функций errors.Wrap и errors.Cause. Они делают код более читаемым.

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

 

func Cause(err error) error {
root := err
for {
if err = errors.Unwrap(root); err == nil {
return root
}
root = err
}
}

 

Таким образом, к вопросу обработки ошибок очень важно подходить с особой тщательностью. Нужно передавать полный контекст в функции и обращать внимание на возвращаемый тип.

Популярное

Самые популярные посты

Как быть максимально продуктивным на удалённой работе?
Business

Как быть максимально продуктивным на удалённой работе?

Я запустил собственный бизнес и намеренно сделал всё возможное, чтобы работать из любой точки мира. Иногда я сижу с своём кабинете с большим 27-дюймовым монитором в своей квартире в г. Чебоксары. Иногда я нахожусь в офисе или в каком-нибудь кафе в другом городе.

Привет! Меня зовут Сергей Емельянов и я трудоголик
Business PHP

Привет! Меня зовут Сергей Емельянов и я трудоголик

Я программист. В душе я предприниматель. Я начал зарабатывать деньги с 11 лет, в суровые 90-е годы, сдавая стеклотару в местный магазин и обменивая её на сладости. Я зарабатывал столько, что хватало на разные вкусняшки.

Акция! Профессиональный разработчик CRM за 2000 руб. в час

Выделю время под ваш проект. Знания технологий Vtiger CRM, SuiteCRM, Laravel, Vue.js, Golang, React.js. Предлагаю варианты сотрудничества, которые помогут вам воспользоваться преимуществами внешнего опыта, оптимизировать затраты и снизить риски. Полная прозрачность всех этапов работы и учёт временных затрат. Оплачивайте только рабочие часы разработки после приемки задачи. Экономьте на платежах по его содержанию разработчика в штате. Возможно заключение договора по ИП. С чего начать, чтобы нанять профессионального разработчика на full-time? Просто заполните форму!

Telegram
@sergeyem
Telephone
+4915211100235