00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Errori Personalizzati in Go

Go permette di creare errori personalizzati implementando l’interfaccia error. Questo è utile per fornire contesto aggiuntivo e permettere al chiamante di gestire errori specifici.

L’Interfaccia error

L’interfaccia error in Go è molto semplice:

type error interface {
    Error() string
}

Qualsiasi tipo che implementa il metodo Error() string è un errore valido.

Errori Semplici

Per errori semplici, usa errors.New o fmt.Errorf:

import "errors"

var ErrNonTrovato = errors.New("elemento non trovato")
var ErrNonAutorizzato = errors.New("accesso non autorizzato")

func cerca(id int) (string, error) {
    if id <= 0 {
        return "", ErrNonTrovato
    }
    return "risultato", nil
}

Errori Sentinella

Gli errori sentinella sono variabili di errore predefinite che possono essere confrontate:

var (
    ErrDivisionePerZero = errors.New("divisione per zero")
    ErrFuoriRange       = errors.New("indice fuori range")
)

func dividi(a, b float64) (float64, error) {
    if b == 0 {
        return 0, ErrDivisionePerZero
    }
    return a / b, nil
}

// Confronto con errors.Is
_, err := dividi(10, 0)
if errors.Is(err, ErrDivisionePerZero) {
    fmt.Println("Impossibile dividere per zero")
}

Tipo di Errore Personalizzato

Per errori che necessitano di contesto aggiuntivo, crea un tipo struct:

type ErroreValidazione struct {
    Campo    string
    Valore   string
    Messagio string
}

func (e *ErroreValidazione) Error() string {
    return fmt.Sprintf("validazione fallita per %s='%s': %s",
        e.Campo, e.Valore, e.Messagio)
}

func validaEmail(email string) error {
    if !strings.Contains(email, "@") {
        return &ErroreValidazione{
            Campo:    "email",
            Valore:   email,
            Messagio: "deve contenere @",
        }
    }
    return nil
}

Usare errors.As per Estrarre il Tipo

err := validaEmail("invalida")

var errVal *ErroreValidazione
if errors.As(err, &errVal) {
    fmt.Println("Campo:", errVal.Campo)
    fmt.Println("Valore:", errVal.Valore)
}

Wrapping degli Errori

Avvolgi gli errori per aggiungere contesto mantenendo l’errore originale:

func caricaUtente(id int) (*Utente, error) {
    dati, err := leggiDaDB(id)
    if err != nil {
        return nil, fmt.Errorf("caricaUtente id=%d: %w", id, err)
    }
    return parsaUtente(dati)
}

// L'errore originale è ancora accessibile con errors.Is e errors.As
err := caricaUtente(42)
if errors.Is(err, sql.ErrNoRows) {
    fmt.Println("Utente non trovato nel database")
}

Implementare Unwrap

Per errori personalizzati con wrapping, implementa il metodo Unwrap:

type ErroreOperazione struct {
    Op  string
    Err error
}

func (e *ErroreOperazione) Error() string {
    return fmt.Sprintf("operazione %s fallita: %v", e.Op, e.Err)
}

func (e *ErroreOperazione) Unwrap() error {
    return e.Err
}

Best Practice

  • Usa errori sentinella per condizioni note e confrontabili
  • Usa tipi personalizzati quando serve contesto aggiuntivo
  • Avvolgi gli errori con %w per mantenere la catena degli errori
  • Controlla gli errori con errors.Is (per valore) e errors.As (per tipo)
  • Non esporre dettagli interni all’utente finale

Conclusione

Gli errori personalizzati in Go offrono un meccanismo potente e flessibile per gestire le condizioni di errore. Dalla semplice stringa ai tipi complessi con wrapping, Go fornisce gli strumenti per creare una gerarchia di errori chiara e manutenibile.