Menu
Coddy logo textTech

Panic, Defer e Recover

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 57 de 107.

Embora o Go enfatize o retorno de erros, algumas situações são verdadeiramente irrecuperáveis. Para esses casos, o Go fornece panic, defer e recover — um mecanismo para lidar com circunstâncias excepcionais.

panic interrompe a execução normal imediatamente. É reservado para erros de programação, como acessar um índice fora dos limites, não para erros esperados, como entrada de usuário inválida:

func MustGetConfig(key string) string {
    value, exists := config[key]
    if !exists {
        panic("missing required config: " + key)
    }
    return value
}

defer agenda uma função para ser executada quando a função envolvente retorna — seja normalmente ou devido a um pânico. As chamadas adiadas são executadas em ordem inversa (último a entrar, primeiro a sair):

func ProcessFile() {
    fmt.Println("Opening file")
    defer fmt.Println("Closing file")
    fmt.Println("Processing...")
    // Saída: Opening file, Processing..., Closing file
}

recover captura um panic e retorna a execução normal. Ele só funciona dentro de uma função deferred:

func SafeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

Este padrão converte um panic em um erro comum, permitindo que seu programa continue. Use-o com moderação — prefira retornar erros para casos de falha esperados e reserve panic/recover para situações verdadeiramente excepcionais, como estado corrompido ou condições irrecuperáveis.

challenge icon

Desafio

Fácil

Vamos construir uma calculadora de divisão segura que demonstra como usar panic, defer, e recover para lidar com situações excepcionais de forma graciosa. Sua calculadora tentará operações arriscadas e converterá pânicos em erros regulares que podem ser tratados normalmente.

Você organizará seu código em dois arquivos:

  • calculator.go: Crie a lógica de divisão com recuperação de pânico.

    Implemente uma função Divide que recebe dois inteiros e entra em pânico com a mensagem "division by zero" se o divisor for zero. Caso contrário, ela retorna o resultado inteiro da divisão.

    Implemente uma função SafeDivide que recebe dois inteiros e retorna tanto um resultado inteiro quanto um erro. Esta função deve:

    • Usar defer com uma função anônima para se recuperar de qualquer pânico
    • Se um pânico for recuperado, convertê-lo em um erro com o formato "calculation error: [panic message]"
    • Chamar a função Divide internamente
    • Retornar o resultado e nil se for bem-sucedido, ou zero e o erro se um pânico foi recuperado
  • main.go: Leia dois inteiros da entrada e use SafeDivide para realizar o cálculo com segurança. Também demonstre o defer imprimindo mensagens de limpeza na ordem correta.

    Sua função principal deve:

    • Imprimir Starting calculation no início
    • Usar defer para agendar a impressão de Cleanup complete
    • Usar outro defer para agendar a impressão de Releasing resources
    • Chamar SafeDivide com os valores de entrada
    • Imprimir Result: [value] ou Error: [error message] com base no resultado

As seguintes entradas serão fornecidas:

  • Linha 1: Primeiro inteiro (dividendo)
  • Linha 2: Segundo inteiro (divisor)

Lembre-se de que as chamadas adiadas (deferred calls) são executadas na ordem inversa (last-in, first-out), portanto, suas mensagens de limpeza devem aparecer na ordem oposta àquela em que foram adiadas.

Por exemplo, dados 20 e 4, sua saída deve ser:

Starting calculation
Result: 5
Releasing resources
Cleanup complete

E dados 10 e 0, sua saída deve ser:

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

Folha de consulta

Go fornece panic, defer e recover para lidar com circunstâncias excepcionais.

panic interrompe a execução normal imediatamente e é reservado para erros de programação irrecuperáveis:

func MustGetConfig(key string) string {
    value, exists := config[key]
    if !exists {
        panic("missing required config: " + key)
    }
    return value
}

defer agenda uma função para ser executada quando a função envolvente retorna. Chamadas adiadas (deferred) são executadas em ordem inversa (última a entrar, primeira a sair):

func ProcessFile() {
    fmt.Println("Opening file")
    defer fmt.Println("Closing file")
    fmt.Println("Processing...")
    // Saída: Opening file, Processing..., Closing file
}

recover captura um panic e retorna a execução normal. Ele só funciona dentro de uma função adiada (deferred):

func SafeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

Este padrão converte um panic em um erro comum. Prefira retornar erros para falhas esperadas e reserve panic/recover para situações verdadeiramente excepcionais.

Experimente você mesmo

package main

import (
	"fmt"
)

func main() {
	// Ler entrada
	var dividend, divisor int
	fmt.Scanln(&dividend)
	fmt.Scanln(&divisor)

	// Imprimir mensagem inicial
	fmt.Println("Starting calculation")

	// TODO: Usar defer para agendar a mensagem "Cleanup complete"

	// TODO: Usar defer para agendar a mensagem "Releasing resources"
	// Lembre-se: chamadas adiadas (deferred) executam na ordem LIFO (last-in, first-out)

	// TODO: Chamar SafeDivide com os valores de entrada

	// TODO: Imprimir "Result: [value]" ou "Error: [error message]"
	// com base no fato de um erro ter sido retornado ou não
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos