Panic, Defer e Recover
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 57 di 107.
Sebbene Go dia importanza alla restituzione degli errori, alcune situazioni sono davvero irrecuperabili. Per questi casi, Go mette a disposizione panic, defer e recover: un meccanismo per gestire circostanze eccezionali.
panic interrompe immediatamente la normale esecuzione. È riservato agli errori di programmazione, come l'accesso a un indice fuori dai limiti, non agli errori previsti, come un input utente non valido:
func MustGetConfig(key string) string {
value, exists := config[key]
if !exists {
panic("missing required config: " + key)
}
return value
}defer pianifica l'esecuzione di una funzione quando la funzione circostante termina, normalmente o a causa di un panic. Le chiamate differite vengono eseguite in ordine inverso (ultimo entrato, primo uscito):
func ProcessFile() {
fmt.Println("Opening file")
defer fmt.Println("Closing file")
fmt.Println("Processing...")
// Output: Opening file, Processing..., Closing file
}recover intercetta un panic e riprende la normale esecuzione. Funziona solo all'interno di una funzione differita:
func SafeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic: %v", r)
}
}()
riskyOperation()
return nil
}Questo schema converte un panic in un errore normale, consentendo al programma di continuare. Usalo con parsimonia: preferisci restituire errori nei casi di errore previsti e riserva panic/recover a situazioni davvero eccezionali, come uno stato corrotto o condizioni irrecuperabili.
Sfida
FacileCostruiamo una calcolatrice per divisioni sicure che mostri come usare panic, defer e recover per gestire con eleganza le situazioni eccezionali. La calcolatrice proverà a eseguire operazioni rischiose e convertirà i panic in errori normali che possono essere gestiti normalmente.
Organizzerai il codice in due file:
calculator.go: crea la logica di divisione con recupero dai panic.Implementa una funzione
Divideche accetta due numeri interi e genera un panic con il messaggio"division by zero"se il divisore è zero. Altrimenti, restituisce il risultato intero della divisione.Implementa una funzione
SafeDivideche accetta due numeri interi e restituisce sia un risultato intero sia un errore. Questa funzione dovrebbe:- Usare
defercon una funzione anonima per recuperare da qualsiasi panic - Se viene recuperato un panic, convertirlo in un errore con il formato
"calculation error: [panic message]" - Chiamare internamente la funzione
Divide - Restituire il risultato e
nilin caso di successo, oppure zero e l'errore se è stato recuperato un panic
- Usare
main.go: leggi due numeri interi dall'input e usaSafeDivideper eseguire il calcolo in sicurezza. Mostra anchedeferstampando messaggi di pulizia nell'ordine corretto.La funzione principale dovrebbe:
- Stampare
Starting calculationall'inizio - Usare
deferper programmare la stampa diCleanup complete - Usare un altro
deferper programmare la stampa diReleasing resources - Chiamare
SafeDividecon i valori di input - Stampare
Result: [value]oppureError: [error message]in base all'esito
- Stampare
Verranno forniti i seguenti input:
- Riga 1: primo numero intero (dividendo)
- Riga 2: secondo numero intero (divisore)
Ricorda che le chiamate differite vengono eseguite in ordine inverso (ultimo entrato, primo uscito), quindi i messaggi di pulizia dovrebbero apparire nell'ordine opposto rispetto a quello in cui sono stati differiti.
Per esempio, dati 20 e 4, l'output dovrebbe essere:
Starting calculation
Result: 5
Releasing resources
Cleanup completeE dati 10 e 0, l'output dovrebbe essere:
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup completeProvalo tu
package main
import (
"fmt"
)
func main() {
// Leggi l'input
var dividend, divisor int
fmt.Scanln(÷nd)
fmt.Scanln(&divisor)
// Stampa il messaggio iniziale
fmt.Println("Starting calculation")
// TODO: Usa defer per programmare il messaggio "Cleanup complete"
// TODO: Usa defer per programmare il messaggio "Releasing resources"
// Ricorda: le chiamate differite vengono eseguite in ordine LIFO (l'ultima inserita è la prima a essere eseguita)
// TODO: Chiama SafeDivide con i valori di input
// TODO: Stampa "Result: [value]" oppure "Error: [error message]"
// in base al fatto che sia stato restituito un errore
}
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