0Pricing
Go Academy · Lezione

Wrapping e unwrap degli errori

Utilizzare %w, errors.Is ed errors.As

Wrapping e unwrap degli errori è una lezione Go Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Go Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Go Academy include 4 lezioni in totale.

Che cos'è il wrapping degli errori?

Il wrapping degli errori incorpora un errore originale in un nuovo errore, preservandolo per l'analisi programmatica e aggiungendo al contempo il contesto. È stato introdotto correttamente in Go 1.13:

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

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

Il verbo %w

Utilizzi %w (non %v) in fmt.Errorf per eseguire il wrapping di un errore. L'errore sottoposto a wrapping è accessibile tramite errors.Unwrap:

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

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

errors.Unwrap restituisce l'errore successivo nella catena, ovvero l'errore sottoposto a wrapping, oppure nil se non ce n'è alcuno:

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

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is — Percorrere la catena

errors.Is(err, target) rimuove ricorsivamente il wrapping della catena degli errori finché non trova una corrispondenza o raggiunge nil:

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

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

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As — Percorrere la catena per tipo

errors.As(err, &target) percorre la catena cercando un errore assegnabile al tipo di target:

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

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

Unwrap personalizzato per le catene

Implementi Unwrap() error nel tipo di errore personalizzato per supportare errors.Is ed errors.As:

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

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

%v e %w in fmt.Errorf

La differenza tra %v e %w:

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

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

Wrapping nella pratica: gestore HTTP

Un wrapping coerente degli errori facilita il debug nei sistemi di produzione:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join — Wrapping multiplo (Go 1.20+)

errors.Join crea un errore che esegue il wrapping di più errori. errors.Is li controlla tutti:

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

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

Best practice per il wrapping degli errori

Linee guida per il wrapping degli errori:

  • Esegua il wrapping con %w per preservare l'errore e consentirne l'analisi programmatica
  • Aggiunga il nome dell'operazione come contesto: fmt.Errorf("funcName: %w", err)
  • Non esegua il wrapping degli errori sentinella se i chiamanti non utilizzeranno errors.Is
  • Eviti di aggiungere due volte lo stesso contesto a ogni livello
  • Implementi Unwrap() nei tipi personalizzati per partecipare alle catene

Verifica rapida

Qual è la differenza tra l'utilizzo di %v e %w in fmt.Errorf?

Riepilogo: wrapping e unwrapping degli errori

Riepilogo:

  • fmt.Errorf("ctx: %w", err) esegue il wrapping con contesto
  • errors.Unwrap recupera l'errore successivo nella catena
  • errors.Is percorre la catena per cercare una corrispondenza di identità
  • errors.As percorre la catena per cercare una corrispondenza di tipo
  • I tipi personalizzati implementano Unwrap() error
  • errors.Join esegue il wrapping di più errori (Go 1.20+)

Domande Frequenti

La lezione «Wrapping e unwrap degli errori» è gratuita?

Sì — il testo completo di «Wrapping e unwrap degli errori» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Go Academy, passa a CoddyKit PRO. Il corso Go Academy include 4 lezioni in totale.

Cosa imparerò in «Wrapping e unwrap degli errori»?

Utilizzare %w, errors.Is ed errors.As Eserciti Go Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Go Academy?

Non è richiesta alcuna esperienza precedente. Go Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Wrapping e unwrap degli errori»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Go Academy?

Sì. Ogni lezione Go Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. L'interfaccia error
  2. Creare errori personalizzati
  3. Wrapping e unwrap degli errori
  4. panic, recover e defer
← Torna a Go Academy