Ошибки (основы)
Ошибки как значения в Go — возврат и проверка ошибок, errors.New и fmt.Errorf, идиома `if err != nil` и почему ошибка — обычное значение, а не исключение.
4 вопросов
JuniorТеорияОчень частоКак функция в Go сообщает вызывающему коду об ошибке?
Как функция в Go сообщает вызывающему коду об ошибке?
Функция возвращает значение типа error последним, рядом с обычными результатами. Ошибка nil означает успех; ненулевая ошибка означает сбой, и остальные результаты могут быть непригодны. Вызывающий код проверяет ошибку и решает, что делать дальше — для обычных ошибок исключений нет, поэтому сбои передаются через обычные возвращаемые значения, а не через отдельный канал throw/catch.
Типичные ошибки
- ✗Ожидать, что Go бросит исключение при обычном сбое, а не вернёт ошибку
- ✗Использовать остальные возвращаемые значения при ненулевой ошибке, когда они могут быть невалидны
- ✗Считать ненулевую ошибку успехом, раз функция всё же что-то вернула
Уточняющие вопросы
- →Какими должны быть остальные результаты, когда функция возвращает ненулевую ошибку?
- →Когда
panicуместнее, чем возврат ошибки?
MiddleТеорияОчень частоПочему идиоматичный Go проверяет if err != nil сразу после каждого вызова?
Почему идиоматичный Go проверяет if err != nil сразу после каждого вызова?
Поскольку ошибки — это значения, поток управления явный: вы обязаны посмотреть на каждую сами. Проверка сразу и ранний возврат не дают действовать над результатом, который после сбоя может быть невалиден. Это ещё и держит happy path без отступов — ветка успеха течёт прямо вниз, а возвраты по ошибке отслаиваются в сторону. Игнорирование ошибки через _ молча скрывает сбой — это и есть баг, который идиома предотвращает.
Типичные ошибки
- ✗Откладывать все проверки ошибок на конец вместо проверки сразу после вызова
- ✗Отбрасывать ошибку через
_и действовать над результатом, который может быть невалиден - ✗Вкладывать happy path внутрь ветки ошибки, выворачивая идиому наизнанку
Уточняющие вопросы
- →Когда действительно безопасно проигнорировать возвращённую ошибку через
_? - →Как ранний
returnпо ошибке держит happy path без отступов?
JuniorТеорияЧастоПочему ошибка в Go — это значение, а не исключение?
Почему ошибка в Go — это значение, а не исключение?
Ошибка в Go — это обычное значение интерфейса, возвращаемое из функции. Поскольку это просто значение, вы храните его, передаёте, сравниваете и исследуете обычным кодом — без особого синтаксиса try/catch. Это делает обработку сбоев явной и видимой на каждом месте вызова. А panic зарезервирован для по-настоящему исключительных, невосстановимых ситуаций, а не для рутинных ошибок.
Типичные ошибки
- ✗Воспринимать ошибки Go как брошенные исключения, автоматически идущие вверх по стеку
- ✗Хвататься за
panic, чтобы сигнализировать об обычных, ожидаемых сбоях - ✗Считать, что ошибкам нужен особый синтаксис, а не обычный код работы со значениями
Уточняющие вопросы
- →Что именно встроенный интерфейс
errorтребует реализовать у типа? - →Когда
panicоправдан и как сюда вписываетсяrecover?
JuniorТеорияЧастоКак в Go создать новое значение ошибки, с контекстом или без него?
Как в Go создать новое значение ошибки, с контекстом или без него?
Используйте errors.New("message") для фиксированного, статического сообщения или fmt.Errorf("context: %v", x), когда нужно вставить детали в текст. Оба возвращают значение, удовлетворяющее интерфейсу error. А fmt.Errorf с глаголом %w дополнительно оборачивает существующую ошибку, поэтому вызывающий код позже развернёт её через errors.Is или errors.As, чтобы увидеть исходную причину.
Типичные ошибки
- ✗Думать, что
fmt.Errorfлишь печатает текст, а не возвращает значениеerror - ✗Использовать
%vтам, где нужно оборачивание, теряя цепочку unwrap, которую хранит%w - ✗Хвататься за
panicдля создания ошибки вместоerrors.New/fmt.Errorf
Уточняющие вопросы
- →В чём разница между форматированием ошибки через
%vи через%w? - →Как
errors.Isиerrors.Asиспользуют цепочку, которую строит%w?