- PVSM.RU - https://www.pvsm.ru -
// гофер пытается найти логику среди обработки ошибок
+-------+-------+-------+-------+-------+-------+
| | err | | err | | err |
| ,_,,, | | | | |
| (◉ _ ◉) | | | | |
| /) ( | | | |
| "" "" | | | |
+ +-------+ +-------+ +-------+
| | err | err | | err |
| | | | | |
| | | | | |
+-------+ +-------+ +-------+ +
| err | | err |
| | | |
| | | |
+ +-------+ + +-------+ +
| | err | | err | logic |
| | | | | |
| | | | | |
+-------+-------+-------+-------+-------+-------+
Я пишу на Go несколько лет, в Каруне многие вещи сделаны на нём; язык мне нравится своей простотой, незамысловатой прямолинейностью и приличной эффективностью. На других языках я писать не хочу.
Но сорян, к бесконечным if err != nil
я до конца привыкнуть так и не смог.
Да-да, я знаю все аргументы: явное лучше неявного, язык Go многословен, зато понятен, и всё такое. Но, блин, на мой взгляд Го-вэй Го-вэю рознь.
Одно дело отказываться от магических ORM или всеобъемлющих фреймворков — мы получаем больше контроля, а платим за это неким "лишним" техническим кодом, который особо никому и не мешает. Немного медитативного набивания кода — это даже приятно.
Но совсем другое дело, когда бизнес-логика, которая и так бывает очень сложна, замусоривается обработкой ошибок. Дело вообще не в том, что мне лень написать бойлерплейт, я с этим абсолютно ок. Дело в читабельности.
Возьмем самый-самый простой пример (псевдокод):
Вместо такого
прочитали из базы()
обработали()
записали результат()
Мы имеем
прочитали из базы()
if err != nil {
return fmt.Errorf("не смогли прочитать из базы: %w", err)
}
обработали()
if err != nil {
return fmt.Errorf("не смогли обработать: %w", err)
}
записали результат()
if err != nil {
return fmt.Errorf("не смогли записать результат: %w", err)
}
Какой из двух кусков кода боле читабелен?
Да, второй более explicit, и да, он досконально проверяет всю ошибочную фигню, но зато в первом примере сразу понятно, что происходит, а во втором приходится продираться через мусор, потому что сама логика происходящего занимает намного меньше площади экрана, чем ошибочные реакции. А если сама логика сложна и содержит какие-то хитроумные условия и подсчёты? Там капец просто.
И это, знаете ли, big fucking deal. Как вы, наверно, знаете, при программировании люди тратят на чтение кода 80-90% времени, а на написание совсем чуть-чуть. Т.е. сначала надо разобраться, что уже происходит, и лишь потом добавлять новое. Так вот, с чтением кода в Go совсем беда. И беда эта связана только с обработкой ошибок, всё остальное — в пределах нормы.
Стандартный пакет errors не сохраняет стек вызовов, поэтому, когда вы в конце концов на самом высоком уровне получили ошибку и записали её в лог, в логе вы просто так не поймёте, где изначально была проблема. Например, ошибка была "ошибка SQL запроса". Где sql, какой именно запрос из сотен? А трейс есть только на момент записи лога, остальной стек уже потерян. Именно поэтому люди вынуждены выкручиваться: использовать сторонние пакеты или прояснять ошибку вручную, добавляя информацию на каждом слое (через fmt.Errorf) или логировать прямо в месте ошибки (ещё больше захламляя логику).
В общем, на практике вместо хотя бы
if err != nil {
return nil, err
}
чаще всего идёт оборачивание
if err != nil {
return nil, fmt.Errorf("мы тут делали то-то и то-то, а нам вернули ошибку: %w", err)
}
Причём это объяснение в 99% нужно не для лучшего понимания происходящего, а тупо для того, чтобы потом по сообщению в логе найти место возникшей проблемы.
Есть несколько десятков proposals [1], которые предлагают разные способы упрощения языка.
Например, ключевое слово try [2] перед вызовом функции, которое работает практически как макрос, неявно добавляющий if err != nil {return nil, err}.
или так:
callSomeFunction() orfail
(см здесь [3])
Тысячи их. Как по мне, надо сделать этот самый try. Но его недостаточно, потому что никто не возвращает просто ошибку, её возвращают с пояснениями. Поэтому нужен try, запоминающий цепочку вызовов. И вот это уже будет бомба.
Статья написана по мотивам поста из канала Cross Join [4].
Автор: Антон Околелов
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/errors/392884
Ссылки в тексте:
[1] несколько десятков proposals: https://github.com/golang/go/issues?q=is%3Aopen+label%3Aerror-handling++label%3Aproposal
[2] ключевое слово try: https://github.com/golang/go/issues/32437
[3] здесь: https://github.com/golang/go/issues/67955
[4] Cross Join: https://t.me/crossjoin
[5] Источник: https://habr.com/ru/companies/karuna/articles/830346/?utm_source=habrahabr&utm_medium=rss&utm_campaign=830346
Нажмите здесь для печати.