-
Notifications
You must be signed in to change notification settings - Fork 27
[2025 27 03] Teletext. Error Handling in Go
Ярославна Поздникина Всем привет и добро пожаловать на Телетекст! Передаю слово Валентину Чащину, руководителю отдела серверной разработки Go в ecom.tech (ex. Samokat.tech). Валентин будет отправлять информацию текстовыми сообщениями. Реагируйте, задавайте вопросы в этом же чате, поставив в начале тег #вопрос. Автору лучшего вопроса подарим рюкзак Хекслета с доставкой по РФ или промокод на доступ к нашим курсам (на выбор победителя).
Валентин Чащин Всем привет!
Действительно в программировании на любом языке ошибки неизбежны. Они могут возникнуть из-за проблем с сетью, некорректных входных данных или багов в коде. В большинстве языков (например, Java, Python) для обработки ошибок используются исключения (exceptions). Однако в Go принципиально другой подход, кто знает какой?
Stanislav errors as values
Валентин Чащин
Верно: errors as values, то есть ошибки обрабатываются явно, как значения, без использования исключений.
Ошибки в программах – это нормальное явление, и их обработка является важной частью программирования. Если код не умеет правильно реагировать на ошибки, это может привести к сбоям, утечкам памяти, повреждению данных и другим критическим проблемам. В Go разработчики поощряются к явному управлению ошибками, что делает код предсказуемым и понятным.
Валентин Чащин В этом уроке мы разберем:
- Почему в Go нет исключений в классическом понимании.
- Как работать с ошибками (error).
- Использование defer, panic и recover.
- Лучшие практики обработки ошибок.
Валентин Чащин
Почему в Go нет исключений?
В большинстве языков программирования исключения (exceptions) используются для обработки неожиданных ситуаций. В Go было принято решение не использовать исключения, так как они скрывают поток выполнения программы и делают код менее предсказуемым.
Валентин Чащин
Основные причины отказа от исключений:
- Явность. Ошибки передаются явно, что делает код легче для анализа и тестирования.
- Производительность. Исключения требуют затрат на обработку (stack unwinding), тогда как в Go ошибки представляют собой обычные значения.
- Контроль потока исполнения. Исключения могут вызывать неожиданные изменения в ходе выполнения программы. В Go ошибки передаются явно, обеспечивая детерминированность кода.
- Обслуживаемость. Код с исключениями часто требует дополнительного анализа (поиск в стеке вызовов), в то время как явная передача ошибок позволяет легко их отслеживать.
Валентин Чащин Кто-нибудь разрабатывал программы на Java?
Stanislav о, да!
Валентин Чащин Тогда следующий пример будет показательным) Дадим время другим участникам прочитать сообщения выше.
Предлагаю, как прочитали, ставить сердечко на сообщение)
Валентин Чащин Отлично, механика работает, погнали дальше)
Валентин Чащин
Исключения vs Ошибки в Go
Пример кода на Java с исключениями:
try {
int result = divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("Ошибка: " + e.getMessage());
}
Аналогичный код на Go:
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("деление на ноль")
}
return a / b, nil
}
Здесь ошибка передается в виде значения error, а не бросается исключением.
Валентин Чащин Ошибки далее будут на русском для упрощения, но, конечно, в реальных программах обычно мы пишем их на английском) Просто для консистентности с другими ошибками, порождаемыми не нами)
Валентин Чащин
Паники как аналоги исключений
Хотя в Go нет try-catch, паника (panic) по сути аналогична исключению в других языках. Оба механизма прерывают выполнение кода и передают управление обработчику ошибок (recover в Go или catch в других языках). Разница в том, что в Go паники используются крайне редко, только для критических ситуаций, а исключения в других языках – повсеместно.
Валентин Чащин
Работа с error
В Go ошибки представлены интерфейсом error:
type error interface {
Error() string
}
Создать ошибку можно с помощью пакета errors:
import (
"errors"
"fmt"
)
func main() {
err := errors.New("что-то пошло не так")
fmt.Println(err)
}
николай catman #вопрос а что будет критической ситуацией? Я например делал тестовое на го, сверялся с жпт. Он писал код аля "не подключились к БД - паник". И если есть recover - все же можно использовать это как аналог try/catch? Какие плюсы минусы у данного подхода? Не считая того, что поток выполнения изменяется? Все равно не понимаю, почему в go сделано так, тк это раздувает код в матрешку 🤔(если что я на пхп пишу)
Валентин Чащин Тут мы немного коснулись темы интерфейсов. В Go мы с ними работаем немного не так как в языках с классическим ООП или языках, типа TypeScript. Если тут есть вопросы, не стесняйтесь их задавать)
Хай-тех пацан #вопрос А насколько правильный подход в Go вешать recover глобально, что бы если где-то выскочит паника, то приложение не завершилось, а продолжило работу?
Просто в той же Java довольно часто так и делают. Но доводилось слышать, что в Go так не принято.
Валентин Чащин На вопросы чуть позже отвечу, в конце, чтоб сейчас не потерять нить, ок?)
Валентин Чащин
Возвращение ошибок из функций
В Go принято возвращать ошибки явно через return. Такой подход делает код более читаемым и предсказуемым. Пример:
func readFile(filename string) (string, error) {
if filename == "" {
return "", errors.New("имя файла не может быть пустым")
}
return "Содержимое файла", nil
}
Stanislav #вопрос наверное, корректнее будет сравнить и с error в java тоже? А так будто бы остается простор для использования panic-recover в стиле обычного try-catch
Nikita A. Паники потребляют довольно много ресурсов и не очень удобны в плане обработки ошибок вообще
Ярославна Поздникина ставьте в начале тег #вопрос - потом по всем пройдемся
Yan Shkurinskiy Если до конца честным быть - не во всех языках, есть немало языков где использование обычных значений для кодирования ошибки лучше сделано
Валентин Чащин Сравнение языков предлагаю сегодня не обсуждать, это бесконечная тема в которой невозможно прийти к единому мнению)
Валентин Чащин Итак, мы разобрались, как создавать ошибки, но иногда нам нужно обогащать чужие ошибки
Валентин Чащин
Оборачиваем ошибки
Часто требуется передавать дополнительные сведения об ошибках. Для этого используется fmt.Errorf с форматом "ошибка: %w", позволяя вкладывать одну ошибку в другую:
err := fmt.Errorf("ошибка загрузки данных: %w", originalErr)
Для анализа ошибок применяются errors.Is и errors.As:
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("Файл не найден")
}
Валентин Чащин У меня тут два сообщения склеились, так что я их сейчас разобью для удобства)
Валентин Чащин
defer, panic и recover
defer
defer позволяет отложить выполнение кода до завершения функции. Это удобно при работе с файлами, соединениями и другими ресурсами. Пример:
func example() {
defer fmt.Println("Этот код выполнится в конце")
fmt.Println("Основная логика")
}
panic
panic используется в крайних случаях, когда программа не может продолжать работу. Пример:
func main() {
panic("критическая ошибка")
}
recover
recover позволяет поймать panic и предотвратить падение программы:
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Восстановление после паники:", r)
}
}()
panic("случилась паника!")
}
Андрюха Шляпников #вопрос если ошибка обёрнута в другую ошибку как проверить тип внутренней ошибки? Как правильно проверять?
Stanislav #вопрос тяжело дается понимание различий между errors.Is и errors.As. Там они как-то по-разному завязаны на иерархию ошибки, но до конца не доходит. Был бы рад, если б пояснили
николай catman #вопрос в go нет иерархии ошибок? Например если работаем с БД или сетевые запросы делаем - как отличить одну ошибку от другой и обработать их по разному?
Валентин Чащин Радует, что много вопросов, на все постараюсь ответить уже совсем скоро)
А пока давайте пойдём дальше к теме, которую по непонятной мне причине даже не все гоферы знают)
Валентин Чащин
Фатальные ошибки, которые нельзя отловить через recover
Некоторые ошибки приводят к немедленному завершению программы, и recover тут не поможет. Кто может привести примеры таких?
Yan Shkurinskiy в го это кажется (мне) как компромисс
в языках с типами-суммами типа haskell/rust, где ошибки можно энкодить корректнее (не давая на руки сразу два значения, а вынуждая на уровне типов проверить) приходится обменять развертку стека на регулярный чекинг "а что же там?", тут возможно не сразу очевидно, что будет дешевле (по перфу)
в го типы-суммы не используются, но надо на nil чекать. Я на го не пишу, незнаю как это соотносится с перфом)
Nikita A. log.Fatal()?
Валентин Чащин Вангую, как все пошли гуглить или чатджипитить))
николай catman #вопрос ошибки в го могут раскрывать какие-то чувствительные данные? пароли-явки и тд? Может есть либы и тд, которые могут это сделать? в PHP, емнип, может засветиться пароль при работе с БД
Валентин Чащин Ну, log.Fatal — это не ошибка, это явное намерение завершить программу с записью в лог)
николай catman деление на ноль? :D
Хай-тех пацан Подозреваю, что какой-нибудь out of memory.
Валентин Чащин Давайте дам подсказку)
В каких случаях нам не имеет смысла восстанавливать работу программы? Что может такого произойти, что мы такие «на этом мои полномочия — всё»?
Yan Shkurinskiy тоже про оом думаю
Валентин Чащин Отлично! Ещё?
Yan Shkurinskiy возможно разного рода сегфолты
Sergеу B обращение к неинициализированной мапе?
Валентин Чащин Ну в целом, правильные ответы есть, это здорово)
Примеры фатальных ошибок:
- out of memory (runtime: out of memory)
- stack overflow (runtime: goroutine stack exceeds limit)
- fatal error: all goroutines are asleep - deadlock! Эти ошибки возникают на уровне рантайма и не могут быть обработаны через recover, потому что Go сразу завершает процесс.
Тим Вот такое может быть при конкуретном обращении к мапе. (Насколько помню не паника)
Хай-тех пацан Я думаю, что если обобщить, то это такие ошибки, после которых нет смысла продолжать выполнения программы. Команды от операционной системы, оут оф мемори, может быть ещё какие-то проблемы с вычислительными ресурсами.
Валентин Чащин 5 баллов Гриффиндору) На этом вопросе реально даже опытные гоферы иногда падают)
Андрюха Шляпников Обращение к нулевому указателю? Типа как в плюсах
Хай-тех пацан Не, это обычная паника будет.
Тим А мы ловили такое на проде))) После таких случаев навсегда откладывается в голове))
Валентин Чащин Нет, в этом случае мы вполне можем продолжать работать, ведь у нас и память есть, и стек — и всё вот это вот)
Валентин Чащин Ну давайте подведём итоги коротким примером, а потом перейдём к вопросам)
Валентин Чащин
Практический пример
Допустим, у нас есть функция, читающая файл:
func readFile(filename string) (string, error) {
if filename == "" {
return "", errors.New("имя файла не может быть пустым")
}
return "Содержимое файла", nil
}
Затем вызываем её и обрабатываем ошибку:
func main() {
content, err := readFile("")
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println(content)
}
Валентин Чащин Мне сказали, что у нас здесь есть студенты, кто прямо сейчас проходит курсы по Go в Хекслете, для вас - вопросы для самопроверки, чтобы закрепить материал
Вопросы для самопроверки:
- Почему в Go нет исключений?
- Как объявить и вернуть ошибку в Go?
- Чем panic отличается от error?
- Как работает recover?
- Когда следует использовать defer?
Валентин Чащин Ошибки разобрали, готов на ответить на ваши вопросы по Go, необязательно по сегодняшней теме.
николай catman #вопрос что почитать на тему работы с ошибками, бест практис и тд, имею ввиду объявление переменных и тому подобное, просто чтобы код писать.
Yan Shkurinskiy #вопрос есть ли в го какие-то способы статического анализа кода чтобы не забыть проверить err на nil?
николай catman это будет на собеседовании? 😢
Хай-тех пацан По-моему, стандартный гошный линтр это делает.
Тим #вопрос Где-то есть список всех фатальных ошибок?
Yan Shkurinskiy а что проверяет? существует ли if где мы проверили err?
Хай-тех пацан По-моему, да.
Валентин Чащин Не получится, потому что recover работает в пределах свой горутины, а, например, http-запросы обрабатываются в разных горутинах — до основной горутины в данном случае паника не дойдёт)
Валентин Чащин В целом да, разница только в том, что try-catch используется повсеместно, а panic-recover рекомендуется не использовать без явной необходимости
MTX go vet стд либа
Nikita #вопрос .О некоторых ошибках (особенно в бизнес-логике) необходимо оповестить клиента, вернув их в ответе API. Напримре, для http api указать конкретный http status code = 400 и тело ответа
{error_code: ORDER_ALREADY_SHIPPED}
В каком слое/модуле приложения будет наиболее правильно логически и удобно технически определять характер ответа API (читай, хардкодить status code и error code).
- Будет ли это отдельный файл с описанием ошибок и маппингом на упомянутые два code)?
- Или же описывать где-то в отдельных request handler ах, но тогда как соблюсти унификацию?
- Или же погружать прямо в модули, работающие с бизнес логикой? Не происходил ли смешение зон отвентсвнности, бизнес-логика начинает что-то знать про протокол взаимодействия приложений.
- какое-то еще композитное решение?
И поверх всего еще контрольный вопрос, по сути, к каждому из подходов. Обеспечивает ли он возможность без боли и с большой долей точности создать полный список ошибок, которое в принципе может возвращать API во всех возможных сценариях?
николай catman тут не подскажу, но спрошу у ребят про это
Валентин Чащин Тут всё просто) errors.Is проверяет значение, а errors.As — тип. То есть если мы объявили какой-то кастомный тип ошибки6 реализовав интерфейс error, и мы не может проверить ошибку по значению, потому что там, например, всегда динамические данные, то есть смысл использовать errors.As, а если мы можем сравнить ошибку с какой-то переменной (в том числе при вложенных, обёрнутых, ошибках), то нам достаточно errors.Is — и это в большинстве случаев так и будет)
MTX #вопрос есть ли либы/подходы для более лаконичной обработки ошибок, может приемы какие? монады? еще что-то?
Тим #вопрос Как лучше разделять ошибки - например для клиента и какие-то внутрненние сервисные. (Например для логов и ответа веб-сервера)? Ну и второй - как лучше локализировать ошибки для различных языков?
Валентин Чащин Как раз выше ответил про errors.Is/As — это оно) Выше так же есть пример, где мы оборачиваем ошибки при помочи fmt.Errorf
Yan Shkurinskiy монады в го невозможны в силу ограниченности системы типов
Валентин Чащин Очень хороший вопрос! Да, сам по себе механизм ошибок чувствительные данные никак не маскирует, об этом должен позаботиться разработчик на этапе логирования ошибки
Константин Кузин а можно пример?
Андрюха Шляпников https://stepik.org/course/187490/syllabus
На степике есть бесплатный курс от Васи Романова, курс сам по себе отличный, но домашки хардовые
Валентин Чащин Конечно, есть линтеры, которые это делают. Стандартном является мультилинтер golangci-lint
Константин Кузин а там текстом как то можно отобразить уроки? видео формат не очень удобен
николай catman https://go.dev/blog/errors-are-values
Stanislav #вопрос и общий и по теме: интересно как же наконец осилить (более менее) паттерны многопоточки (с горутинами, каналами, группами) и обработки ошибок, с ней связанных, если пока не было продакшн проекта на го. Может быть, есть какие-то толковые задачки в какой-нибудь из книжек или где-то еще
Валентин Чащин Я не встречал такого, если честно) Только в исходниках смотреть. Ну или задать вопрос на stackoverflow — может кто-то их уже собрал)
Андрюха Шляпников Нет текста нет, но там можно скачать архив, с домашками, к каждой теме собрано дофига разных материалов
Валентин Чащин Это как раз причина, по которой в Go принято обрабатывать ошибки явно. Поскольку мы можем легко проверять ошибки на значения/типы, нам не составляет труда отдавать соответствующие ответы.
Что касается слоя, то это вопрос к архитектуре конкретного приложения, но в целом, если мы говорим об ошибках бизнес-логики, то есть смысл обогащать ошибки соответствующим контекстом именно на уровне бизнес-логики (юзкейсы, сущности). Для этого иногда полезно определить свой тип ошибки, реализовав интерфейс error
MTX #вопрос все же в итоге можно чем-то заменить постоянно повторяющиеся ??
if err != nil {
return err // or maybe break
}
Валентин Чащин Нет) Есть попытки что-то подобное сделать (обычно от разработчиков с других стеков), но это, скорее, потакание собственным привычкам (не осуждаю).
Я бы рекомендовал всё-таки писать на языке в том стиле, в котором принято.
Очень важно понимать, что нормально написанная программа на любом другом языке на самом деле будет такой же избыточной (если не избыточней) в части кода, проверяющего ошибки. Просто часто в других языках разработчики не проверяют ошибки, отдавая это на уровень какого-нибудь глобального exception handler-а. Но это путь в никуда, не надо так делать нигде)
Валентин Чащин Это легко сделать, опять же, реализовав интерфейс error в какой-то собственной структуре, которая будет по заданным правилам оформлять одну и ту же ошибку: клиентам отдавать одно, в логи писать другое и т.д.
Yan Shkurinskiy Это немного неправда, есть примеры где и менее избыточно, локанично, легко гуглится, не буду называть конкретные названия)
MTX http сервер?
Yan Shkurinskiy Другой вопрос что нужно принять, что го и не старается в эту стороны (это не хорошо и не плохо, это - данность)
Валентин Чащин
errors.As
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main() {
if _, err := os.Open("non-existing"); err != nil {
var pathError *fs.PathError
if errors.As(err, &pathError) {
fmt.Println("Failed at path:", pathError.Path)
} else {
fmt.Println(err)
}
}
}
errors.Is
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main() {
if _, err := os.Open("non-existing"); err != nil {
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("file does not exist")
} else {
fmt.Println(err)
}
}
}
Sergey Melnikov #вопрос как определить что необходимость явная? очень часто это субъективное восприятие, для кого-то и передача неверного параметра в функцию явная необходимость.
Валентин Чащин Я бы не рекомендовал смотреть на это, как на цель осиливать эти паттерны) Лучше поищи задачу, где тебе пригодится многозадачность, и потом найди паттерн под неё — так ты быстрей поймёшь саму суть)
Тим Так и предполагал)
Валентин Чащин В этом нет необходимости, ты сэкономишь не так много времени, как кажется, но сильно усложнишь поддержку программы, которая будет использовать какие-то кастомные функции, меняющий поток выполнения)
Sergey Melnikov #вопрос Можно подробнее объяснить почему это путь в никуда и так делать не надо, если 80% ошибок, что в го, что в языках с исключениями приводят к внутренней ошибке функционала и обрабатывание в каждой функции на каждом слое этих ошибок приводит к разросшемуся коду.
Валентин Чащин Хорошо
Yan Shkurinskiy Чтобы не было недопониманий) У меня цель тут не сказать что в го плохо с ошибками, а сказать что "можно и чуть дальше пойти, со своими компромиссами")
Yan Shkurinskiy Там лаконичность на другое обменивается в итоге, всё не бесплатно
Валентин Чащин Явная необходимости на самом деле только одна — когда ты не можешь вернуть ошибку, потому что это сломает интерфейс. То есть у тебя есть функция, сигнатура которой не содержит error в возврате, и ты не имеешь возможности поменять сигнатуру интерфейса.
Во всех остальныйх случаях don't panic)
Sergey Melnikov т.е. если какой-то плохой программист (или по дизайну) решил не давать возможности возвращать ошибку, а ты должен использовать этот контракт - ты используешь панику? следует - что паника может быть везде, в любой внешней либе, а значит её надо везде обрабатывать, чтобы не привести свою систему к DOS.
В итоге, вместо только обработки ошибок, мне в любом месте надо еще учитывать, что метод может вернуть панику, соответственно ничем не отличается от проблем с исключениями
Yan Shkurinskiy иногда это не плохой программист, иногда это по дизайну "не должно возвращать ошибку никогда", а потом пришёл бизнес и всё поменялось)
Sergey Melnikov Случайно нажал Enter - не дописал
Валентин Чащин Во-первых, потому что количество кода не является проблемой. Нет такого правила ни фактического, ни эмпирического, что кода должно быть мало. Есть желание программистов меньше писать — это понимаю, но это не правило, а хотелка)
Путь в никуда это по той причине, что обработка ошибок в глобальных хендлерах превращается в огромное дерево принятия решений (!) без возможности вернуться поток исполнения программы.
В итоге обычно всё это скатывается в отсутствие обработки и простое сохранение ошибки в логи или какой-нибудь sentry)
Надо просто принять, что если мы хотим писать надёжные программы, то ошибки придётся обрабатывать в месте их возникновения.
Валентин Чащин К сожалению, вывод верный. Но не всё так плохо. В комьюнити авторы библиотек стараются идти по тем же принципам и не паниковать)
Yan Shkurinskiy Кстати хорошее замечание
Тут я бы так сказал
Такой приём, в общем случае, когда мы с помощью значений энкодим ошибки, позволяет нам поделить ошибки на два класса:
- Явные, с которыми бы хотелось поработать, принять, что-то сделать на уровне логики программы, возможно что-то разное в зависимости от места вызова
- Неявные, работать мы с ними не хотим, если оно случилось, всё равно где - падаем до первого хендлера, чёт пишем в логи и завершаемся
Я бы сказал, что стоит так ментально для себя определить разницу между value errors и одним из сортов exceptions
Ярославна Поздникина @xfoby спасибо тебе огромное! Выбери, пожалуйста, автора лучшего вопроса или просто самого активного участника - мы его наградим. Чат остается здесь, просто дальнейшее общение здесь не регламентируется.
Валентин Чащин Я обычно просто анализирую используемые библиотеки и смотрю, есть ли там паника) Это в общем-то простым поиском по гитхабу решается и снимает все вопросы к использованию либы)
Sergey Melnikov Во-первых, потому что количество кода не является проблемой. Изучить 100 строчек кода сложнее чем 30 строчек кода, так что, учитывая, что код во много раз чаще читается, чем пишется - выглядит проблемой.
Путь в никуда это по той причине, что обработка ошибок в глобальных хендлерах превращается в огромное дерево принятия решений (!) без возможности вернуться поток исполнения программы. Наверно, я не удачно выразился. не значит, что все ошибки должны обрабываться только глобально. Они должны обрабатываться в какой-то манере, чтобы проинформировать эксплуатирующего систему. Но отказываться от глобальной обработки и требовать обрабатывать их по месту усложняет восприятие кода при чтении (за счет увеличенных, повторяющихся и дублирующихся конструкций)
В итоге обычно всё это скатывается в отсутствие обработки и простое сохранение ошибки в логи или какой-нибудь sentry) Это номрально и решает почти все исключительные ситуации (сетевые проблемы например)
Валентин Чащин Сек)
Yan Shkurinskiy Ну, видите, я на го не пишу, поэтому у меня чуть другой подход, но ментально так как я описал)
Ярославна Поздникина Всем, кто регистрировался в боте, придет ссылку на анкету обратной связи - заполните ее, пожалуйста, нам это важно ❤️
Валентин Чащин Вот этот вопрос мне понравился больше всего. Остальные тоже были хороши, но этот прям жизненный)
Ярославна Поздникина Принято, спасибо!
Валентин Чащин Когда ты много пишешь на Go, ты привыкаешь к постоянным if err != nil и глаз их пропускает, так что на анализе кода это не сказывается. При этом, если тебе надо именно поток отследить, то такой подход наоборот повышает анализируемость кода в отличие от try .. catch просто потому что try .. catch заставляют тебя перемещаться глазами по коду иногда достаточно далеко от точки возникновения исключения до места его обработки
Sergey Melnikov А вот тут обратная проблема, я могу определить свои ошибки как явные с которыми я предалагаю работать и позволить что-то сделать вызывающему коду. Но вызываемому коду это не нужно и он будет просто писать в сентри и возвращать внутреннюю ошибку, потому что ему интересен толкьо успешный результат. В итоге, по этой логике я должен возвращать панику, что точно не хороший вариант :)
Основная мысль - обработка ошибко и принятие решение - это проблема кода, который вызывает функциюю, а не который возвращает состояние ошибки. И двойственный подход явно усложняет обработку этих ошибок
Sergey Melnikov а что на счет зависимостей, которые эти библиотеки используют? и зависимостей зависимостей?
Yan Shkurinskiy Ну не то что бы должен)
Тут зависит от дизайна, иногда не хочется давать возможность, так как это "протечка абстракций" именно в этой либе именно с твоим взглядом
Валентин Чащин Рекурсивно проверяем — это не сложно, учитывая то, что авторы библиотек обычно, повторюсь, не используют паники)
Валентин Чащин Всем большое спасибо за интересные вопросы! До встречи)
Yan Shkurinskiy Усложняет, безусловно
Тут есть парочка решений на этот вопрос
Sergey Melnikov еще одна проблема, когда кто-то написал
if err == nil {
// Do something
}
не в качестве опечатки, а в качестве логики
Yan Shkurinskiy Но в общем случае - согласен, надо постоянно думать, что тут делать - панику или вэлью эррор
Yan Shkurinskiy Два механизма вместо одного
Sergey Melnikov подход наоборот повышает анализируемость кода в отличие от try .. catch просто потому что try .. catch заставляют тебя перемещаться глазами по коду иногда достаточно далеко от точки возникновения исключения до места его обработки try catch нужно писать только в тех местах, где ты явно хочешь обработать ошибку. И это прям очень явно видно, что здесь хотят обработать ошибку
куча
if err != nil {
return err
}
делает места с явной обработкой ошибки менее очевидным
николай catman Валентин, а вы используете copilot или аналоги в работе? В теории ии этот код может изи дополнять.
Sergey Melnikov хех, к сожалению, я так ловил DOS. было очень больно. Дополнительно, что код бросающий панику был в функции errors.As (в версии 1.13, сейчас не знаю, надеюсь убрали)
Валентин Чащин Не-не! Надо просто не использовать панику, вот и всё)
Валентин Чащин Я не готов холиварить. Мне не близка позиция в которой пообщяется неявная обработка ошибок
николай catman А еще есть вопрос не связанный с обработкой ошибок, а вообще по Go - что изменилось в поведении языка, что с 1.20 первый запуск тестов стал долгим? Как это можно пофиксить (нужно для практик на код бейзиксе, там время на выполнение ограничено). Раньше за секунду все запускалось, а на свежих версиях - на первом запуске тесты думают-кешируют, потом только запускаются.
Sergey Melnikov это звучит, как чтобы было чисто - просто не мусорьте. Красивое и правильное определение, но люди чаще чем хотелось бы нарушают правила :(
Валентин Чащин Не-а, большинство ассистентов требуют загрузки кодовой базы на свои сервера, а это нарушение коммерческой тайны)
А локальные ассистенты на сегодняшний день не достаточно хороши. Ну во всяком случае те, с которыми я знаком)
Валентин Чащин Если честно, не сталкивался. В Go есть механизм кэширования тестов, возможно, в какой-то конкретной ситуации он не отрабатывает, но у меня такого не было — все тесты летают)