Ответ
В Go обработка ошибок — это фундаментальная часть языка, основанная на идее, что ошибки являются обычными значениями, а не исключениями. Это поощряет явную проверку ошибок в коде.
Основные подходы:
-
Явная проверка
if err != nil
Это самый распространенный и идиоматичный способ в Go. Функции, которые могут завершиться с ошибкой, возвращают её в качестве последнего значения. Вызывающий код обязан проверить это значение.file, err := os.Open("filename.txt") if err != nil { log.Printf("Не удалось открыть файл: %v", err) return err }
-
Создание собственных ошибок Для описания специфических сбоев в вашем домене создаются кастомные ошибки.
errors.New()
: для создания простых статических ошибок.fmt.Errorf()
: для создания ошибок с динамическим текстом.var ErrUserNotFound = errors.New("пользователь не найден")
func GetUser(id int) (*User, error) { if id <= 0 { return nil, fmt.Errorf("некорректный ID пользователя: %d", id) } // ... логика поиска if user == nil { return nil, ErrUserNotFound } return user, nil }
-
Оборачивание ошибок (Wrapping) Начиная с Go 1.13, появилась возможность оборачивать ошибки с помощью директивы
%w
вfmt.Errorf
. Это позволяет сохранить контекст исходной ошибки.if err != nil { return fmt.Errorf("ошибка при чтении данных пользователя: %w", err) }
-
Проверка типа и значения ошибки Пакет
errors
предоставляет инструменты для инспекции цепочки обернутых ошибок:errors.Is(err, target)
: проверяет, есть ли в цепочке ошибок конкретная ошибкаtarget
(например,ErrUserNotFound
).errors.As(err, &target)
: проверяет, имеет ли ошибка в цепочке конкретный тип, и присваивает её переменнойtarget
._, err := GetUser(1) if errors.Is(err, ErrUserNotFound) { // Обработка случая, когда пользователь не найден }
var pathError *fs.PathError if errors.As(err, &pathError) { // Обработка ошибки, связанной с путем к файлу fmt.Println("Ошибка в пути:", pathError.Path) }
-
Паника и восстановление (
panic
/recover
) Этот механизм используется только для исключительных, невосстановимых ситуаций, таких как критические ошибки во время инициализации или ошибки программиста (например, выход за пределы среза). Его не следует использовать для обычной обработки ошибок, таких как сбой сетевого запроса или отсутствие файла.defer func() { if r := recover(); r != nil { fmt.Println("Произошла паника, но мы её обработали:", r) } }() panic("произошло что-то непоправимое")