0Pricing
Go Academy · Lekcja

Tworzenie własnych błędów

errors.New, fmt.Errorf i błędy sentinelowe

Tworzenie własnych błędów to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Go Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Go Academy zawiera 4 lekcji w sumie.

Po co tworzyć własne typy błędów?

Własne typy błędów przechowują ustrukturyzowane dane wykraczające poza komunikat tekstowy, dzięki czemu kod wywołujący może analizować konkretne warunki błędów i odpowiednio na nie reagować:

package main
import "fmt"

// Custom error type carries extra context
type HTTPError struct {
    StatusCode int
    Message    string
}

func (e *HTTPError) Error() string {
    return fmt.Sprintf("HTTP %d: %s", e.StatusCode, e.Message)
}

func fetch(url string) error {
    return &HTTPError{404, "page not found"}
}

func main() {
    err := fetch("http://example.com")
    fmt.Println(err) // HTTP 404: page not found
}

Implementowanie interfejsu error

Każdy typ, który ma metodę Error() string, spełnia wymagania interfejsu error:

package main
import "fmt"

type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: field %q value %v — %s", e.Field, e.Value, e.Message)
}

func validateAge(age int) error {
    if age < 0 || age > 150 {
        return &ValidationError{"age", age, "must be between 0 and 150"}
    }
    return nil
}

func main() {
    fmt.Println(validateAge(-5))
}

Błędy sentinelowe z errors.New

Błędy sentinelowe to zmienne na poziomie pakietu reprezentujące dobrze znane warunki błędów:

package main
import ("fmt"; "errors")

var (
    ErrNotFound      = errors.New("not found")
    ErrUnauthorized  = errors.New("unauthorized")
    ErrAlreadyExists = errors.New("already exists")
)

func getUser(id int) (string, error) {
    if id != 1 { return "", ErrNotFound }
    return "Alice", nil
}

func main() {
    _, err := getUser(99)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("user does not exist")
    }
}

fmt.Errorf z %w do opakowywania błędów

fmt.Errorf z %w opakowuje błąd, zachowując oryginalny błąd na potrzeby errors.Is i errors.As:

package main
import ("fmt"; "errors")

var ErrDatabase = errors.New("database error")

func queryDB() error {
    return fmt.Errorf("queryDB: %w", ErrDatabase)
}

func main() {
    err := queryDB()
    fmt.Println(err)                       // queryDB: database error
    fmt.Println(errors.Is(err, ErrDatabase)) // true — unwrapped
}

Własny błąd z Unwrap

Zaimplementuj Unwrap() error we własnym typie błędu, aby uczestniczył w łańcuchach błędów:

package main
import ("fmt"; "errors")

var ErrNotFound = errors.New("not found")

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string {
    return fmt.Sprintf("query %q: %v", e.Query, e.Err)
}
func (e *QueryError) Unwrap() error { return e.Err }

func main() {
    err := &QueryError{"SELECT *", ErrNotFound}
    fmt.Println(err)
    fmt.Println(errors.Is(err, ErrNotFound)) // true
}

errors.Is — sprawdzanie tożsamości

errors.Is przechodzi po łańcuchu błędów w poszukiwaniu błędu równego wartości docelowej:

package main
import ("fmt"; "errors")

var ErrTimeout = errors.New("timeout")

func deepCall() error {
    return fmt.Errorf("layer3: %w", fmt.Errorf("layer2: %w", ErrTimeout))
}

func main() {
    err := deepCall()
    fmt.Println(errors.Is(err, ErrTimeout)) // true — found deep in chain
}

errors.As — wyodrębnianie typu

errors.As znajduje pierwszy błąd w łańcuchu pasujący do typu docelowego i przypisuje go:

package main
import ("fmt"; "errors")

type PermissionError struct{ User string }
func (e *PermissionError) Error() string { return "permission denied for " + e.User }

func action(user string) error {
    return fmt.Errorf("action: %w", &PermissionError{user})
}

func main() {
    err := action("bob")
    var pe *PermissionError
    if errors.As(err, &pe) {
        fmt.Println("denied:", pe.User) // denied: bob
    }
}

Opakowywanie wielu błędów (Go 1.20+)

W Go 1.20 dodano errors.Join oraz fmt.Errorf z wieloma wystąpieniami %w do łączenia błędów:

package main
import ("fmt"; "errors")

func validate(name, email string) error {
    var errs []error
    if name == ""  { errs = append(errs, errors.New("name required")) }
    if email == "" { errs = append(errs, errors.New("email required")) }
    return errors.Join(errs...)
}

func main() {
    err := validate("", "")
    fmt.Println(err)
    // name required
    // email required
}

Konwencje nazewnictwa typów błędów

Konwencje dotyczące zmiennych i typów błędów w Go:

  • Zmienne błędów sentinelowych: ErrXxx (np. ErrNotFound)
  • Własne typy błędów: XxxError (np. ValidationError)
  • Zmienna na poziomie pakietu dla błędu sentinelowego, typ wskaźnikowy dla błędów zawierających dodatkowe dane
  • Eksportuj je, aby kod wywołujący mógł używać ich z errors.Is / errors.As

Kiedy stosować poszczególne podejścia

Wybór właściwego podejścia do obsługi błędów:

  • errors.New — proste błędy bez dodatkowych danych, sprawdzanie tożsamości błędu sentinelowego
  • fmt.Errorf — sformatowany komunikat z opcjonalnym opakowaniem
  • Typ własny — gdy kod wywołujący potrzebuje ustrukturyzowanych danych (pól, kodów)
  • errors.Join — agregowanie wielu błędów walidacji

Szybkie sprawdzenie

Która funkcja umożliwia wyodrębnienie konkretnego typu błędu z łańcucha błędów?

Podsumowanie: własne błędy

Podsumowanie:

  • Zaimplementuj Error() string, aby tworzyć własne typy błędów
  • Dodaj Unwrap() error, aby uczestniczyć w łańcuchach błędów
  • Używaj errors.Is do sprawdzania tożsamości, a errors.As do wyodrębniania typu
  • Błędy sentinelowe: ErrXxx; typy własne: XxxError
  • errors.Join agreguje wiele błędów (Go 1.20+)

Często zadawane pytania

Czy lekcja „Tworzenie własnych błędów” jest bezpłatna?

Tak — pełny tekst „Tworzenie własnych błędów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Go Academy, przejdź na CoddyKit PRO. Kurs Go Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Tworzenie własnych błędów”?

errors.New, fmt.Errorf i błędy sentinelowe Ćwiczysz Go Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Go Academy?

Nie wymagamy żadnego doświadczenia. Go Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Tworzenie własnych błędów”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Go Academy?

Tak. Każda lekcja Go Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Interfejs error
  2. Tworzenie własnych błędów
  3. Opakowywanie i rozpakowywanie błędów
  4. panic, recover i defer
← Powrót do Go Academy