Ответ
В Go используется идиоматический подход к обработке ошибок, основанный на явной проверке и возврате значений типа error
.
Основной принцип: функции, которые могут завершиться с ошибкой, возвращают её в качестве последнего значения в кортеже. Успешное выполнение обозначается возвратом nil
в этой позиции.
func divide(a, b float64) (float64, error) {
if b == 0 {
// Используем fmt.Errorf для создания форматированной ошибки
return 0, fmt.Errorf("деление на ноль: делимое = %.2f", a)
}
return a / b, nil // Ошибки нет, возвращаем nil
}
// Вызов и проверка
result, err := divide(10, 0)
if err != nil {
// Ошибка произошла, обрабатываем её
fmt.Println("Произошла ошибка:", err)
}
Способы создания ошибок:
errors.New()
: Создает простую ошибку с фиксированным текстовым сообщением. Идеально для статических ошибок.var ErrDivisionByZero = errors.New("деление на ноль")
fmt.Errorf()
: Создает ошибку с форматированием, какfmt.Sprintf
. Позволяет добавлять в сообщение контекст (например, значения переменных).
Обертывание ошибок (Error Wrapping)
Начиная с Go 1.13, появилась возможность "оборачивать" ошибки, чтобы сохранить исходную причину. Это делается с помощью директивы %w
в fmt.Errorf
.
func readFile() error {
err := openFile()
if err != nil {
// Оборачиваем исходную ошибку, добавляя контекст
return fmt.Errorf("не удалось прочитать файл: %w", err)
}
return nil
}
Проверка ошибок
Для проверки "обернутых" ошибок используются функции из пакета errors
:
errors.Is(err, target)
: Проверяет, есть ли в цепочке ошибокerr
ошибка, равнаяtarget
. Используется для сравнения с конкретными экземплярами ошибок (например,io.EOF
).errors.As(err, &target)
: Проверяет, есть ли в цепочкеerr
ошибка, которую можно присвоить переменнойtarget
. Используется для работы с кастомными типами ошибок.
Пример с кастомным типом:
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintf("код: %d, сообщение: %s", e.Code, e.Msg)
}
func doSomething() error {
return &MyError{Code: 500, Msg: "внутренняя ошибка сервера"}
}
func main() {
err := doSomething()
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Printf("Перехвачена кастомная ошибка. Код: %dn", myErr.Code)
}
}