Проверяем Ошибки

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

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

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



Проверяем ошибки

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

Общее требование к авторам пакетов — возвращать ошибки известного общедоступного типа, чтобы вызывающая сторона могла использовать утверждение типа и подробно их изучить.

Я считаю, что такая практика приводит к ряду нежелательных результатов:

  • Открытые типы ошибок увеличивают «зону контакта» с API пакета.

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



Подтвердите ожидаемое поведение, а не тип ошибки

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

Это предложение соответствует природе неявных интерфейсов Go, а не является [подтипом] природы языков, основанных на наследовании.

Рассмотрим этот пример:

   

func isTimeout(err error) bool { type timeout interface { Timeout() bool } te, ok := err.(timeout) return ok && te.Timeout() }

Вызывающий может использовать isTimeout(), чтобы определить, произошла ли ошибка из-за тайм-аута, реализовав интерфейс тайм-аута, а затем подтвердить, произошла ли ошибка из-за тайм-аута - и все это без каких-либо знаний о типе или исходных значениях ошибки источника.

Этот метод позволяет оборачивать ошибки, обычно с помощью библиотек, которые добавляют пояснения к пути ошибки; при условии, что обернутые типы ошибок также реализуют интерфейсы ошибок, которые они обертывают. Это может показаться непреодолимой проблемой, но на практике существует довольно много распространенных методов интерфейса, поэтому Timeout() bool и Temporary() bool охватывают большой набор вариантов использования.



Окончательно

Подтвердите ожидаемое поведение, а не тип ошибки

Для авторов пакетов

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

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



Для пользователей пакета

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

Не спрашивайте авторов пакетов о типах открытых ошибок; попросите их привести свои типы в соответствие с общими интерфейсами, указав соответствующие методы Timeout() или Temporary().



об авторе

Автор этой статьи, Дэйв Чини , является автором многих популярных пакетов Go, таких как https://github.com/pkg/errors И https://github.com/davecheney/httpstat .



От переводчика

Хоть оригинал статьи и датирован концом 2014 года, мне кажется, она не потеряла своей актуальности.

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

Правки в текст для улучшения понятности материала приветствуются! Теги: #Go #golang #обработка ошибок #Обработка ошибок

Вместе с данным постом часто просматривают: