In Go, la panico (panic) è usata per segnalare un errore fatale nel programma. Dopo una chiamata a panic, l'esecuzione inizia con l'unwind dello stack e la chiamata a tutte le funzioni deferred. Se nello stack c'è una funzione con una chiamata a recover(), può intercettare e gestire la panico — ma recover funziona solo all'interno della propria goroutine e solo quando chiamato da defer.
Il pattern raccomandato per un recupero sicuro è:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Recuperato da panic:", r) } }() fn() } go safe(func() { panic("fail!") // non porta all'arresto del programma })
È importante ricordare che:
panic interrompe l'esecuzione della goroutine corrente fino al primo recoverPuò recover intercettare una panico avvenuta in un'altra goroutine?
Risposta: No, recover funziona solo all'interno della goroutine in cui è avvenuta la panico, e solo se chiamato in una funzione deferred. Se la panico si verifica in una goroutine e recover è chiamato in un'altra, non avverrà alcuna intercettazione, il programma si arresta in modo anomalo.
Esempio:
func main() { go func() { panic("dentro la goroutine") }() time.Sleep(time.Second) recover() // Non funzionerà! Panic chiuderà il programma }
Storia
Uno sviluppatore ha implementato la gestione degli errori usando il pattern defer recover() solo nella funzione principale. Quando si è verificata una panico all'interno di un worker in una goroutine separata, l'intero programma è andato in crash — l'errore non è stato intercettato dal recover globale.
Storia
Nel progetto è stato utilizzato defer/recover per un arresto controllato del server web, ritenendo che fosse sufficiente. Si è scoperto che una panico in una delle goroutine logiche terminava l'intero processo, perché la gestione del recover era nel posto sbagliato — è stato necessario rifattorizzare l'intero pool di worker.
Storia
Nel processo di gestione dei messaggi si è verificata una panico a causa di un malfunzionamento della funzione user. Lo sviluppatore si aspettava che fosse "catturata" dal recover di alto livello, ma non si era reso conto che recover funziona SOLO da defer. Di conseguenza, il servizio è andato in crash in modo irregolare quando un messaggio non valido appariva nella coda.