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.
Sfida
FacileCostruiamo 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:
ErrPermissionDeniedcon il messaggio"permission denied"
Crea un tipo di errore personalizzato:
- La struct
FileErrorcon i campiFilename(string) eOperation(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", restituisciErrPermissionDenied. Se filename è"missing.txt", restituisci un*FileErrorcon Operation"read"e il Filename specificato. Altrimenti restituiscinil.ProcessFile(filename string) error- ChiamaReadFile. Se viene restituito un errore, avvolgilo usandofmt.Errorfcon il formato"processing failed: %w". Altrimenti restituiscinil.
main.go: Leggi un nome di file dall'input e chiamaProcessFile. Usaerrors.Is()eerrors.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()trovaErrPermissionDeniednella catena: stampa il messaggio di errore completo, poi stampaAccess denied - check file permissionssu una nuova riga - Se
errors.As()trova un*FileErrornella catena: stampa il messaggio di errore completo, poi stampaFile 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 permissionsE dato missing.txt, il tuo output dovrebbe essere:
processing failed: read failed on file: missing.txt
File issue: missing.txt during readE dato data.txt, il tuo output dovrebbe essere:
File 'data.txt' processed successfullyNota 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
}
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
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online