Обёртывание ошибок
Обёртывание ошибок (error wrapping) в Go — это техника, позволяющая добавлять дополнительный контекст к существующей ошибке, создавая цепочку из вложенных ошибок. Это особенно полезно для передачи информации о том, где и при каких обстоятельствах произошла ошибка, без потери исходной ошибки. Основная цель обёртывания — сохранение возможности проверки исходной ошибки с помощью функций errors.Is и errors.As, даже после добавления контекста.
В Go обёртывание ошибок выполняется с помощью оператора %w в функции fmt.Errorf или с использованием функции errors.Join. Обёрнутая ошибка сохраняет связь с исходной, что позволяет программе проверять, является ли ошибка конкретным типом, или соответствует ли она определённому значению. Основное преимущество обёртывания — возможность добавления контекста (например, имени функции, идентификатора запроса или параметров) без изменения исходной ошибки.
Синтаксис
err := fmt.Errorf("context: %w", originalErr)
В данном примере %w — это специальный форматный вербайзер, который оборачивает ошибку originalErr в новую ошибку с добавленным текстом "context:".
Пример создания обёрнутой ошибки
Рассмотрим пример, где мы оборачиваем ошибку, полученную при попытке открыть файл, добавляя информацию о том, что это произошло при загрузке конфигурации:
package main
import (
"fmt"
"os"
)
func loadConfig() error {
_, err := os.Open("config.json")
if err != nil {
return fmt.Errorf("load config failed: %w", err)
}
return nil
}
func main() {
err := loadConfig()
if err != nil {
fmt.Println(err)
}
}
Результат выполнения кода будет содержать как добавленный контекст, так и исходную ошибку:
"load config failed: open config.json: no such file or directory"
Проверка обёрнутой ошибки с помощью errors.Is
Обёрнутые ошибки можно проверять на соответствие определённому типу или значению с помощью функции errors.Is. Это позволяет обрабатывать ошибки на разных уровнях, не теряя информации о её происхождении:
package main
import (
"errors"
"fmt"
"os"
)
var ErrNotFound = errors.New("file not found")
func loadConfig() error {
_, err := os.Open("config.json")
if errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("load config failed: %w", ErrNotFound)
}
return err
}
func main() {
err := loadConfig()
if errors.Is(err, ErrNotFound) {
fmt.Println("Configuration file is missing")
} else if err != nil {
fmt.Println("Other error:", err)
}
}
Результат выполнения кода:
"Configuration file is missing"
Обёртывание с помощью errors.Join
Начиная с Go 1.20, появилась функция errors.Join, которая позволяет объединять несколько ошибок в одну, оборачивая их все одновременно. Это удобно, когда требуется собрать несколько ошибок в одну цепочку:
package main
import (
"errors"
"fmt"
)
func main() {
err1 := errors.New("database connection failed")
err2 := errors.New("cache read error")
wrapped := errors.Join(err1, err2)
fmt.Println(wrapped)
}
Результат выполнения кода:
"database connection failed\ncache read error"
В этом примере обе ошибки объединяются в одну, и их можно обрабатывать совместно. Однако, в отличие от fmt.Errorf с %w, errors.Join не добавляет текстовый контекст, а просто объединяет несколько ошибок в одну структуру.
Смотрите также
-
функцию
errors.Is,
которая проверяет, содержит ли цепочка ошибок конкретную ошибку -
функцию
errors.As,
которая извлекает ошибку определённого типа из цепочки -
функцию
errors.Unwrap,
которая возвращает предыдущую ошибку в цепочке -
функцию
fmt.Errorf,
которая форматирует строку и может обёртывать ошибки через%w