Opakowywanie błędów (fmt.Errorf)
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 54 z 107.
Gdy błędy przechodzą przez wiele warstw aplikacji, kluczowe staje się ustalenie, gdzie powstał błąd. Funkcja fmt.Errorf języka Go z czasownikiem %w pozwala opakowywać błędy, zachowując oryginalny błąd i dodając kontekst.
Opakowywanie błędów tworzy łańcuch błędów. Każda warstwa może dodać informacje o tym, co próbowała zrobić, gdy wystąpił błąd:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// przetwórz dane...
return nil
}Znacznik formatujący %w jest wyjątkowy. Opakowuje pierwotny błąd w nowy. Różni się od %v, który po prostu zamienia błąd na ciąg znaków i powoduje utratę jego pierwotnej tożsamości.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Gdy LoadSettings kończy się niepowodzeniem, komunikat o błędzie pokazuje pełny łańcuch: "loading settings: reading config file: open settings.json: no such file or directory". Każda warstwa dodaje kontekst, co znacznie ułatwia debugowanie.
Opakowany błąd nadal zawiera w sobie pierwotny błąd. W następnej lekcji dowiesz się, jak rozpakować te błędy i sprawdzić, co zawierają, za pomocą errors.Is() i errors.As().
Wyzwanie
ŁatwyZbudujmy system przetwarzania zamówień, który pokazuje opakowywanie błędów na wielu poziomach. Utworzysz łańcuch funkcji, w którym każda warstwa dodaje kontekst do błędów, dzięki czemu łatwo prześledzić, gdzie dokładnie występują problemy.
Rozmieścisz kod w dwóch plikach:
orders.go: Utwórz logikę przetwarzania zamówień z wieloma warstwami, które opakowują błędy w miarę ich propagowania w górę.Zaimplementuj trzy funkcje tworzące łańcuch przetwarzania:
ValidateItem(itemID string) error— Zwraca błąd z komunikatem"item not found", jeśli itemID ma wartość"INVALID", w przeciwnym razie zwracanilProcessOrder(orderID, itemID string) error— WywołujeValidateItem. Jeśli funkcja zwróci błąd, opakuj go kontekstem:"processing order [orderID]: %w". W przeciwnym razie zwróćnilSubmitOrder(customerName, orderID, itemID string) error— WywołujeProcessOrder. Jeśli funkcja zwróci błąd, opakuj go kontekstem:"submitting order for [customerName]: %w". W przeciwnym razie zwróćnil
Każda warstwa powinna używać
fmt.Errorfz czasownikiem%w, aby opakować błąd z niższej warstwy, tworząc łańcuch kontekstu.main.go: Odczytaj szczegóły zamówienia z danych wejściowych, wywołajSubmitOrderi wyświetl wynik. Jeśli wystąpi błąd, wypisz pełny łańcuch błędów. Jeśli operacja się powiedzie, wypisz komunikat potwierdzający.
Zostaną podane następujące dane wejściowe:
- Wiersz 1: Imię i nazwisko klienta
- Wiersz 2: ID zamówienia
- Wiersz 3: ID produktu
Wypisz wynik:
- Jeśli wystąpi błąd:
Error: [full error chain] - Jeśli operacja się powiedzie:
Order [orderID] submitted successfully for [customerName]
Na przykład dla wartości Alice, ORD-123 i INVALID wynik powinien wyglądać następująco:
Error: submitting order for Alice: processing order ORD-123: item not foundZwróć uwagę, że komunikat o błędzie pokazuje cały łańcuch. Możesz prześledzić problem od próby złożenia zamówienia na najwyższym poziomie, przez przetwarzanie zamówienia, aż po właściwy błąd walidacji. Każda warstwa dodała własny kontekst za pomocą %w.
A dla wartości Bob, ORD-456 i ITEM-001 wynik powinien wyglądać następująco:
Order ORD-456 submitted successfully for BobSpróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Wczytaj nazwę klienta
scanner.Scan()
customerName := scanner.Text()
// Wczytaj identyfikator zamówienia
scanner.Scan()
orderID := scanner.Text()
// Wczytaj identyfikator produktu
scanner.Scan()
itemID := scanner.Text()
// TODO: Wywołaj SubmitOrder z wczytanymi wartościami
// TODO: Jeśli wystąpi błąd, wypisz: Error: [pełny łańcuch błędów]
// TODO: Jeśli operacja się powiedzie, wypisz: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online