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 Go enfatize o retorno de erros, algumas situações são realmente irrecuperáveis. Para esses casos, Go fornece panic, defer e recover: um mecanismo para lidar com circunstâncias excepcionais.

panic interrompe imediatamente a execução normal. É reservado para erros de programação, como acessar um índice fora dos limites, e não para erros esperados, como uma 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 retornar, seja normalmente ou devido a um panic. As chamadas adiadas são executadas na 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 retoma a execução normal. Ele só funciona dentro de uma função adiada:

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 realmente excepcionais, como estado corrompido ou condições irrecuperáveis.

challenge icon

Desafio

Fácil

Vamos criar uma calculadora de divisão segura que demonstra como usar panic, defer e recover para lidar com situações excepcionais de maneira apropriada. Sua calculadora tentará realizar operações arriscadas e converterá os pânicos em erros comuns 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 receba dois inteiros e entre em pânico com a mensagem "division by zero" se o divisor for zero. Caso contrário, ela retornará o resultado inteiro da divisão.

    Implemente uma função SafeDivide que receba dois inteiros e retorne tanto um resultado inteiro quanto um erro. Essa função deve:

    • Usar defer com uma função anônima para recuperar 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-sucedida, ou zero e o erro caso um pânico tenha sido recuperado
  • main.go: Leia dois inteiros da entrada e use SafeDivide para realizar o cálculo com segurança. Demonstre também defer imprimindo mensagens de limpeza na ordem correta.

    Sua função main 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], dependendo do resultado

As seguintes entradas serão fornecidas:

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

Lembre-se de que as chamadas adiadas são executadas na ordem inversa (último a entrar, primeiro a sair), 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
FORMATO DE SAÍDA OBRIGATÓRIO: [Seu conteúdo traduzido aqui]

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: Use defer to schedule "Cleanup complete" message

	// TODO: Use defer to schedule "Releasing resources" message
	// Lembre-se: chamadas adiadas executam em ordem LIFO (last-in, first-out)

	// TODO: Chame SafeDivide com os valores de entrada

	// TODO: Imprima "Result: [value]" ou "Error: [error message]"
	// com base em se um erro foi retornado
}
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

Pratique por conta própria: Compilador de Go online