Menu
Coddy logo textTech

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.

challenge icon

Sfida

Facile

Costruiamo 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 Divide che 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 SafeDivide che accetta due numeri interi e restituisce sia un risultato intero sia un errore. Questa funzione dovrebbe:

    • Usare defer con 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 nil in caso di successo, oppure zero e l'errore se è stato recuperato un panic
  • main.go: leggi due numeri interi dall'input e usa SafeDivide per eseguire il calcolo in sicurezza. Mostra anche defer stampando messaggi di pulizia nell'ordine corretto.

    La funzione principale dovrebbe:

    • Stampare Starting calculation all'inizio
    • Usare defer per programmare la stampa di Cleanup complete
    • Usare un altro defer per programmare la stampa di Releasing resources
    • Chiamare SafeDivide con i valori di input
    • Stampare Result: [value] oppure Error: [error message] in base all'esito

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 complete

E dati 10 e 0, l'output dovrebbe essere:

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete

Provalo tu

package main

import (
	"fmt"
)

func main() {
	// Leggi l'input
	var dividend, divisor int
	fmt.Scanln(&dividend)
	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
}
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