Menu
Coddy logo textTech

Обертывание ошибок (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().

challenge icon

Задание

Легко

Давайте создадим систему обработки order, демонстрирующую оборачивание error на нескольких уровнях. Вы создадите цепочку функций, где каждый уровень добавляет контекст к error, что позволяет легко точно отследить место возникновения проблем.

Вы организуете код в двух файлах:

  • orders.go: Создайте логику processing order с несколькими уровнями, которые оборачивают error по мере его передачи вверх.

    Implement три функции, образующие цепочку processing:

    • ValidateItem(itemID string) error — Возвращает error с сообщением "item not found", если itemID имеет значение "INVALID", в противном случае возвращает nil
    • ProcessOrder(orderID, itemID string) error — Вызывает ValidateItem. Если она возвращает error, оборачивает его контекстом: "processing order [orderID]: %w". В противном случае возвращает nil
    • SubmitOrder(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
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go