Médio Go

Recover em função com defer

Enunciado

Crie uma função safeDivide(a, b int) (result int, err error) que divide a por b. Se b for zero, a função deve entrar em panic e, usando defer e recover, capturar o panic e retornar um erro com a mensagem "divisão por zero". Caso contrário, retorne o resultado e nil.

Requisitos

  • Usar defer com uma função anônima que chama recover.
  • Se b == 0, panic deve ser acionado e recuperado, retornando erro.
  • Se b != 0, retornar o quociente inteiro e nil.
  • O programa principal deve testar ambos os casos e imprimir os resultados.

Código inicial

package main

import "fmt"

func safeDivide(a, b int) (result int, err error) {
	// Seu código aqui
}

func main() {
	// Teste a função
}

Saída esperada

10 / 2 = 5
10 / 0 = erro: divisão por zero
Ver dica

Dentro do defer, verifique se recover() retornou algo não nulo e atribua ao retorno nomeado err.

Mostrar solução
package main

import "fmt"

func safeDivide(a, b int) (result int, err error) {
	// Função anônima adiada para recuperar panic
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%v", r) // converte o valor do panic em erro
		}
	}()

	if b == 0 {
		panic("divisão por zero") // aciona o panic
	}
	return a / b, nil
}

func main() {
	// Caso normal
	res, err := safeDivide(10, 2)
	if err != nil {
		fmt.Printf("10 / 2 = erro: %v\n", err)
	} else {
		fmt.Printf("10 / 2 = %d\n", res)
	}

	// Caso de divisão por zero
	res, err = safeDivide(10, 0)
	if err != nil {
		fmt.Printf("10 / 0 = erro: %v\n", err)
	} else {
		fmt.Printf("10 / 0 = %d\n", res)
	}
}

Passo a passo

  1. A função safeDivide declara retornos nomeados result e err.
  2. Imediatamente, um defer com função anônima é agendado. Dentro dele, recover() captura qualquer panic.
  3. Se b == 0, a função executa panic("divisão por zero"), interrompendo o fluxo normal.
  4. O panic desenrola a pilha e executa o defer. O recover() retorna a string "divisão por zero".
  5. O valor recuperado é atribuído a err usando fmt.Errorf. O retorno nomeado result permanece zero.
  6. A função retorna (0, err).
  7. No main, o erro é verificado e a mensagem apropriada é impressa.

Por que funciona

O defer é executado mesmo quando ocorre um panic, desde que o panic não tenha sido recuperado antes. Como o recover está dentro do defer, ele captura o panic e evita que o programa termine. Os retornos nomeados permitem que o defer modifique err antes do retorno efetivo.

Erros comuns

  • Chamar recover fora de um defer: recover só funciona se chamado diretamente dentro de uma função adiada. Fora disso, retorna nil e o panic continua.
  • Não usar retornos nomeados: se err não for nomeado, o defer não consegue modificar o valor de retorno facilmente.
  • Esquecer de verificar r != nil: se não houver panic, recover() retorna nil; atribuir isso a err sobrescreveria um erro válido.

Outra forma de resolver

Poderíamos retornar um erro diretamente sem usar panic/recover, o que é mais idiomático em Go para erros esperados:

func safeDivide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("divisão por zero")
	}
	return a / b, nil
}

Saída esperada

10 / 2 = 5
10 / 0 = erro: divisão por zero