Menu
Coddy logo textTech

errors.Is() ed errors.As()

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 56 di 107.

Quando gli errori vengono racchiusi, i semplici controlli di uguaglianza come err == ErrNotFound non funzionano più. Il pacchetto errors fornisce due funzioni per esaminare le catene di errori racchiusi: errors.Is() e errors.As().

errors.Is() verifica se un qualsiasi errore nella catena corrisponde a un errore sentinella specifico:

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")
}

Anche se l'errore restituito è avvolto, errors.Is() rimuove gli involucri della catena e trova ErrNotFound al suo interno.

errors.As() verifica se un errore qualsiasi nella catena corrisponde a un tipo specifico ed estrae tale errore:

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)
}

Usa errors.Is() quando controlli gli errori sentinella e errors.As() quando devi accedere ai campi di un tipo di errore personalizzato. Entrambe le funzioni percorrono l’intera catena degli errori, rendendole essenziali per gestire correttamente gli errori racchiusi.

challenge icon

Sfida

Facile

Costruiamo un sistema di elaborazione dei file che dimostra come esaminare correttamente le catene di errori avvolti usando errors.Is() e errors.As(). Creerai errori che verranno avvolti man mano che attraversano diversi livelli, quindi userai queste funzioni per identificarli ed estrarne informazioni.

Organizzerai il codice in due file:

  • errors.go: Definisci l'infrastruttura degli errori per il tuo file system.

    Crea un errore sentinella:

    • ErrPermissionDenied con il messaggio "permission denied"

    Crea un tipo di errore personalizzato:

    • La struct FileError con i campi Filename (string) e Operation (string)
    • Il suo metodo Error() deve restituire: [Operation] failed on file: [Filename]

    Implementa due funzioni che simulano le operazioni sui file e avvolgono gli errori:

    • ReadFile(filename string) error - Se filename è "secret.txt", restituisci ErrPermissionDenied. Se filename è "missing.txt", restituisci un *FileError con Operation "read" e il Filename specificato. Altrimenti restituisci nil.
    • ProcessFile(filename string) error - Chiama ReadFile. Se viene restituito un errore, avvolgilo usando fmt.Errorf con il formato "processing failed: %w". Altrimenti restituisci nil.
  • main.go: Leggi un nome di file dall'input e chiama ProcessFile. Usa errors.Is() e errors.As() per esaminare la catena degli errori avvolti e stampare i messaggi appropriati in base a ciò che trovi al suo interno.

Verrà fornito il seguente input:

  • Riga 1: nome del file da elaborare

Gestisci il risultato esaminando la catena degli errori:

  • Se errors.Is() trova ErrPermissionDenied nella catena: stampa il messaggio di errore completo, poi stampa Access denied - check file permissions su una nuova riga
  • Se errors.As() trova un *FileError nella catena: stampa il messaggio di errore completo, poi stampa File issue: [Filename] during [Operation] su una nuova riga
  • Se non si verifica alcun errore: stampa File '[filename]' processed successfully

Per esempio, dato secret.txt, il tuo output dovrebbe essere:

processing failed: permission denied
Access denied - check file permissions

E dato missing.txt, il tuo output dovrebbe essere:

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

E dato data.txt, il tuo output dovrebbe essere:

File 'data.txt' processed successfully

Nota che, anche se gli errori vengono avvolti da ProcessFile, puoi comunque rilevare l'errore sentinella originale con errors.Is() ed estrarre il tipo di errore personalizzato con errors.As(). Queste funzioni attraversano l'intera catena degli errori per trovare ciò che stai cercando.

Provalo tu

package main

import (
	"errors"
	"fmt"
)

func main() {
	// Leggi il nome del file dall'input
	var filename string
	fmt.Scanln(&filename)

	// Chiama ProcessFile con il nome del file
	err := ProcessFile(filename)

	// TODO: gestisci il risultato esaminando la catena degli errori
	// 
	// Usa errors.Is() per verificare se ErrPermissionDenied è nella catena:
	// - Se trovato: stampa l'errore completo, poi "Access denied - check file permissions"
	//
	// Usa errors.As() per verificare se un *FileError è nella catena:
	// - Se trovato: stampa l'errore completo, poi "File issue: [Filename] during [Operation]"
	//
	// Se non ci sono errori: stampa "File '[filename]' processed successfully"

	_ = errors.Is // suggerimento: usa errors.Is()
	_ = errors.As // suggerimento: usa errors.As()
	_ = err
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Go online