errors.Is() i errors.As()
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 56 z 107.
Gdy błędy są opakowywane, proste sprawdzenia równości, takie jak err == ErrNotFound, przestają działać. Pakiet errors udostępnia dwie funkcje do sprawdzania łańcuchów opakowanych błędów: errors.Is() i errors.As().
errors.Is() sprawdza, czy którykolwiek błąd w łańcuchu pasuje do konkretnego błędu wartowniczego:
import "errors"
var ErrNotFound = errors.New("not found")
func FindUser(id int) error {
return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}
err := FindUser(42)
if errors.Is(err, ErrNotFound) {
fmt.Println("User doesn't exist")
}Mimo że zwrócony błąd jest opakowany, errors.Is() rozwija łańcuch i znajduje w nim ErrNotFound.
errors.As() sprawdza, czy którykolwiek błąd w łańcuchu pasuje do określonego typu, i wyodrębnia go:
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation failed: " + e.Field
}
err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("Invalid field:", ve.Field)
}Używaj errors.Is() do sprawdzania błędów wartowniczych, a errors.As(), gdy potrzebujesz uzyskać dostęp do pól niestandardowego typu błędu. Obie funkcje przechodzą przez cały łańcuch błędów, dzięki czemu są niezbędne do prawidłowej obsługi opakowanych błędów.
Wyzwanie
ŁatwyZbudujemy system przetwarzania plików, który pokaże, jak prawidłowo sprawdzać opakowane łańcuchy błędów za pomocą errors.Is() i errors.As(). Utworzysz błędy, które będą opakowywane podczas przechodzenia przez różne warstwy, a następnie użyjesz tych funkcji, aby je zidentyfikować i wyodrębnić z nich informacje.
Rozmieścisz kod w dwóch plikach:
errors.go: Zdefiniuj infrastrukturę błędów dla systemu plików.Utwórz błąd wartowniczy:
ErrPermissionDeniedz komunikatem"permission denied"
Utwórz własny typ błędu:
- strukturę
FileErrorz polamiFilename(string) iOperation(string) - jej metoda
Error()powinna zwracać:[Operation] failed on file: [Filename]
Zaimplementuj dwie funkcje, które symulują operacje na plikach i opakowują błędy:
ReadFile(filename string) error- Jeśli filename ma wartość"secret.txt", zwróćErrPermissionDenied. Jeśli filename ma wartość"missing.txt", zwróć*FileErrorz Operation o wartości"read"i przekazanym Filename. W przeciwnym razie zwróćnil.ProcessFile(filename string) error- WywołujeReadFile. Jeśli zostanie zwrócony błąd, opakuj go za pomocąfmt.Errorfz formatem"processing failed: %w". W przeciwnym razie zwróćnil.
main.go: Odczytaj nazwę pliku z wejścia i wywołajProcessFile. Użyjerrors.Is()ierrors.As(), aby sprawdzić opakowany łańcuch błędów i wypisać odpowiednie komunikaty na podstawie tego, co znajdziesz w środku.
Otrzymasz następujące dane wejściowe:
- Wiersz 1: nazwa pliku do przetworzenia
Obsłuż wynik, sprawdzając łańcuch błędów:
- Jeśli
errors.Is()znajdzieErrPermissionDeniedw łańcuchu: wypisz pełny komunikat błędu, a następnie w nowym wierszu wypiszAccess denied - check file permissions - Jeśli
errors.As()znajdzie w łańcuchu*FileError: wypisz pełny komunikat błędu, a następnie w nowym wierszu wypiszFile issue: [Filename] during [Operation] - Jeśli nie wystąpi błąd: wypisz
File '[filename]' processed successfully
Na przykład dla secret.txt wynik powinien wyglądać następująco:
processing failed: permission denied
Access denied - check file permissionsDla missing.txt wynik powinien wyglądać następująco:
processing failed: read failed on file: missing.txt
File issue: missing.txt during readA dla data.txt wynik powinien wyglądać następująco:
File 'data.txt' processed successfullyZwróć uwagę, że mimo iż błędy są opakowywane przez ProcessFile, nadal możesz wykryć pierwotny błąd wartowniczy za pomocą errors.Is() i wyodrębnić własny typ błędu za pomocą errors.As(). Te funkcje przechodzą przez cały łańcuch błędów, aby znaleźć to, czego szukasz.
Spróbuj swoich sił
package main
import (
"errors"
"fmt"
)
func main() {
// Odczytaj nazwę pliku z wejścia
var filename string
fmt.Scanln(&filename)
// Wywołaj ProcessFile z nazwą pliku
err := ProcessFile(filename)
// TODO: Obsłuż wynik, analizując łańcuch błędów
//
// Użyj errors.Is(), aby sprawdzić, czy ErrPermissionDenied znajduje się w łańcuchu:
// - Jeśli znaleziono: wypisz pełny błąd, a następnie "Access denied - check file permissions"
//
// Użyj errors.As(), aby sprawdzić, czy *FileError znajduje się w łańcuchu:
// - Jeśli znaleziono: wypisz pełny błąd, a następnie "File issue: [Filename] during [Operation]"
//
// Jeśli nie ma błędu: wypisz "File '[filename]' processed successfully"
_ = errors.Is // wskazówka: użyj errors.Is()
_ = errors.As // wskazówka: użyj errors.As()
_ = err
}
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