Обработка ошибок в Go (Часть 1: Философия и if err != nil)
В Go нет блока try-catch. Ошибка — это просто результат выполнения функции, который вы возвращаете вместе с полезным значением.
1. Интерфейс error
Все ошибки в Go реализуют один простой интерфейс из стандартной библиотеки:
type error interface {
Error() string
}
Это означает, что любая структура или тип, у которого есть метод Error() string, автоматически становится ошибкой.
2. Идиома if err != nil
Поскольку любая функция, которая может привести к ошибке, возвращает её вторым (или последним) параметром, проверка на ошибку стала «визитной карточкой» Go.
func divide(a, b float64) (float64, error) {
if b == 0 {
// Создаем ошибку с помощью стандартного пакета errors
return 0, errors.New("деление на ноль невозможно")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
// ВСЕГДА проверяем err на nil
if err != nil {
fmt.Println("Произошла ошибка:", err)
return // Прерываем выполнение, если работать дальше нельзя
}
fmt.Println("Результат:", result)
}
Почему это кажется многословным?
Да, писать if err != nil приходится часто. Но это заставляет вас явно решать, что делать при ошибке: логировать, пропустить или остановить программу. Нет "невидимых" исключений, которые вылетают из ниоткуда.
3. Как правильно создавать ошибки
Существует три основных способа:
errors.New: Используется для простых статических сообщений.return nil, errors.New("не удалось подключиться к БД")fmt.Errorf: Используется, если нужно добавить в сообщение об ошибке динамические данные (контекст).return nil, fmt.Errorf("пользователь с ID %d не найден", userID)- Пользовательские структуры: Если вам нужно передать вызывающей функции дополнительные данные об ошибке (например, код HTTP-ответа).
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string { return e.Msg }
4. Оборачивание ошибок (Error Wrapping) — это важно!
Часто ошибка возникает на нижнем уровне (например, "файл не найден"), но вы хотите добавить к ней контекст верхнего уровня ("не удалось прочитать конфиг"). В Go 1.13+ для этого используют fmt.Errorf с глаголом %w.
func readConfig() error {
err := os.ReadFile("config.json")
if err != nil {
// Мы "оборачиваем" исходную ошибку, добавляя свой контекст
return fmt.Errorf("ошибка чтения файла: %w", err)
}
return nil
}
Зачем оборачивать? Чтобы вы могли добавить контекст, но при этом сохранить исходную ошибку, чтобы потом проверить её тип (например, "это ошибка прав доступа или ошибка чтения?").