Ответ
В Go принят подход явной обработки ошибок, в отличие от механизма исключений (exceptions) в других языках. Ошибка — это обычное значение, которое функция возвращает наравне с результатом. Это заставляет разработчика осознанно обрабатывать каждый возможный сбой.
Основной паттерн:
Функция, которая может завершиться с ошибкой, возвращает результат и ошибку как последний параметр (value, error)
. Вызывающий код обязан проверить, не является ли ошибка nil
.
import (
"fmt"
"os"
)
f, err := os.Open("filename.txt")
if err != nil {
// Ошибка произошла, обрабатываем её
log.Printf("error opening file: %v", err)
return
}
defer f.Close()
// Ошибки нет, продолжаем работу с f
Ключевые концепции и инструменты:
-
Интерфейс
error
: Любой тип, реализующий методError() string
, удовлетворяет стандартному интерфейсуerror
. Это позволяет создавать кастомные типы ошибок с дополнительной информацией. -
Создание ошибок:
errors.New("сообщение")
: Создает простую ошибку с текстовым сообщением.fmt.Errorf("сообщение с форматом: %w", err)
: Создает ошибку с форматированием. Директива%w
оборачивает (wraps) исходную ошибкуerr
, сохраняя её в цепочке для дальнейшего анализа.
-
Проверка и извлечение ошибок (пакет
errors
):errors.Is(err, target)
: Проверяет, есть ли в цепочке ошибокerr
конкретная ошибкаtarget
(например,io.EOF
). Это предпочтительнее, чемerr == io.EOF
, так как работает с обёрнутыми ошибками.errors.As(err, &target)
: Проверяет, есть ли в цепочке ошибка, которую можно присвоить переменнойtarget
(например, кастомному типу ошибки), и в случае успеха извлекает её.
-
Кастомные типы ошибок: Позволяют переносить больше контекста, чем просто строка.
type MyError struct { Operation string Code int Err error } func (e *MyError) Error() string { return fmt.Sprintf("operation %s failed with code %d: %v", e.Operation, e.Code, e.Err) }