Обертывание ошибок (fmt.Errorf)
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 54 из 107.
Когда errors проходят через несколько уровней вашего приложения, знание того, где возник error, становится крайне важным. Функция Go fmt.Errorf с глаголом %w позволяет оборачивать errors, сохраняя исходный error и одновременно добавляя контекст.
Обёртка ошибок создаёт цепочку ошибок. Каждый уровень может добавлять информацию о том, что он пытался сделать в момент возникновения ошибки:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// обработать данные...
return nil
}Глагол %w особенный. Он оборачивает исходную ошибку в новую. Это отличается от %v, который просто преобразует ошибку в строку и теряет идентичность исходной ошибки.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Когда LoadSettings завершается с ошибкой, сообщение об ошибке показывает полную цепочку: "loading settings: reading config file: open settings.json: no such file or directory". Каждый слой добавляет контекст, что значительно упрощает отладку.
Обернутая ошибка по-прежнему содержит внутри исходную ошибку. В следующем уроке ты узнаешь, как раскрывать такие ошибки и проверять, что находится внутри, с помощью errors.Is() и errors.As().
Задание
ЛегкоДавайте создадим систему обработки order, демонстрирующую оборачивание error на нескольких уровнях. Вы создадите цепочку функций, где каждый уровень добавляет контекст к error, что позволяет легко точно отследить место возникновения проблем.
Вы организуете код в двух файлах:
orders.go: Создайте логику processing order с несколькими уровнями, которые оборачивают error по мере его передачи вверх.Implement три функции, образующие цепочку processing:
ValidateItem(itemID string) error— Возвращает error с сообщением"item not found", если itemID имеет значение"INVALID", в противном случае возвращаетnilProcessOrder(orderID, itemID string) error— ВызываетValidateItem. Если она возвращает error, оборачивает его контекстом:"processing order [orderID]: %w". В противном случае возвращаетnilSubmitOrder(customerName, orderID, itemID string) error— ВызываетProcessOrder. Если она возвращает error, оборачивает его контекстом:"submitting order for [customerName]: %w". В противном случае возвращаетnil
Каждый уровень должен использовать
fmt.Errorfс глаголом%w, чтобы оборачивать error с нижнего уровня и формировать цепочку контекста.main.go: Считайте сведения об order из input, вызовитеSubmitOrderи выведите результат. Если возникает error, выведите полную цепочку error. В случае успеха выведите подтверждающее сообщение.
Будут предоставлены следующие входные данные:
- Строка 1: Имя customer
- Строка 2: Идентификатор order
- Строка 3: Идентификатор item
Выведите результат:
- Если возникает error:
Error: [full error chain] - В случае успеха:
Order [orderID] submitted successfully for [customerName]
Например, если заданы Alice, ORD-123 и INVALID, вывод должен быть таким:
Error: submitting order for Alice: processing order ORD-123: item not foundОбратите внимание, как сообщение об error показывает полную цепочку. Вы можете проследить проблему от попытки отправки на верхнем уровне через processing order до фактической ошибки проверки. Каждый уровень добавил собственный контекст с помощью %w.
А если заданы Bob, ORD-456 и ITEM-001, вывод должен быть таким:
Order ORD-456 submitted successfully for BobПопробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Читать имя клиента
scanner.Scan()
customerName := scanner.Text()
// Читать ID заказа
scanner.Scan()
orderID := scanner.Text()
// Читать ID товара
scanner.Scan()
itemID := scanner.Text()
// TODO: Вызвать SubmitOrder с входными значениями
// TODO: Если возникает ошибка, вывести: Error: [full error chain]
// TODO: If successful, print: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker PoolПотренируйтесь самостоятельно: Онлайн-компилятор Go