Skip to main content


Tratamento de erros​


Normalmente tratamos erros por meio do tipo error, que permite identificar problemas durante a execução e decidir como o programa deve continuar. Já vimos esse comportamento quando trabalhamos com arquivos, verificando o retorno das funções com if err != nil.


Porém, nem todos os problemas são tratados dessa forma, também temos os recursos de panic e recover. Eles permitem interromper a execução normal de uma função e, em determinadas situações, recuperar o programa de uma falha.



Diferença entre error e panic​


O error representa um erro que pode acontecer durante uma operação, como tentar abrir um arquivo que não existe ou receber uma entrada inválida. Nesse caso, a função retorna o erro e o programa pode decidir como tratar ele.

arquivo, err := os.Open("usuarios.txt")

if err != nil {
fmt.Println("Erro ao abrir arquivo:", err)
return
}

defer arquivo.Close()

Já o panic é usado quando acontece uma situação que impede a execução normal do código. Diferentemente de um error, ele interrompe o fluxo normal da função e inicia um processo de encerramento da execução, executando as chamadas defer que estiverem pendentes. Se o panic não for recuperado, o programa será encerrado com uma mensagem de erro e informações sobre onde o problema aconteceu.



O que é panic?​


O panic é uma função nativa que interrompe a execução normal quando ocorre uma situação inesperada. Ele pode ser chamado diretamente pelo código ou acontecer automaticamente, como ao tentar acessar uma posição inexistente de um slice.

package main

import "fmt"

func main() {
fmt.Println("Programa iniciado")

// Provoca um panic.
panic("Ocorreu um erro inesperado")

// Esta instrução não será executada.
// fmt.Println("Programa finalizado")
}

Resultado possível:

Programa iniciado
panic: Ocorreu um erro inesperado

goroutine 1 [running]:
main.main()
/projeto/main.go:9

Além da mensagem, o Go também apresenta o stack trace, que mostra a sequência de chamadas de funções relacionadas ao erro, ajudando a identificar onde ele aconteceu. O panic também pode acontecer sem que seja chamado diretamente.

numeros := []int{10, 20, 30}

// Tenta acessar uma posição que não existe.
fmt.Println(numeros[5])

Nesse caso, o Go provoca um panic porque o slice possui apenas três elementos e estamos tentando acessar uma posição fora dos seus limites.



Recover​


O recover é uma função nativa que permite recuperar a execução de uma goroutine após um panic, evitando que ele encerre o programa quando for tratado corretamente. Para isso, precisamos chamar recover() dentro de uma função executada por defer, durante o processo de recuperação do panic.

package main

import "fmt"

func executarTarefa() {
// Executa esta função quando executarTarefa terminar.
defer func() {
// Verifica se ocorreu um panic.
if err := recover(); err != nil {
fmt.Println("Panic recuperado:", err)
}
}()

fmt.Println("Executando tarefa...")

// Provoca um panic.
panic("Falha durante a execução")
}

func main() {
executarTarefa()

fmt.Println("Programa continua executando")
}

Resultado:

Executando tarefa...
Panic recuperado: Falha durante a execução
Programa continua executando

Nesse exemplo, a função executarTarefa() provoca um panic, mas antes de encerrar sua execução, o Go executa a função registrada com defer. Dentro dela, chamamos:

if err := recover(); err != nil {
fmt.Println("Panic recuperado:", err)
}

O recover() verifica se existe um panic em andamento na mesma goroutine e retorna o valor associado a ele. Se não houver um panic, retorna nil. Como conseguimos recuperar o erro, o programa não é encerrado e a função main() continua executando normalmente. Isso não significa que a função executarTarefa() continuará exatamente de onde aconteceu o panic. Depois da recuperação, ela termina sua execução e o controle volta para a função que a chamou.



Por que usamos recover com defer?​


Como o panic interrompe o fluxo normal de execução, não podemos simplesmente chamar recover() depois dele, porque essa instrução não será alcançada. O defer resolve isso porque suas chamadas pendentes são executadas durante o processamento do panic, permitindo que uma função adiada faça a recuperação.

defer func() {
if err := recover(); err != nil {
fmt.Println("Erro recuperado:", err)
}
}()

Um cuidado é que recover() só consegue recuperar um panic da própria goroutine, quando chamado diretamente por uma função adiada durante o processamento desse panic. Isso significa que, se uma goroutine provocar um panic, não podemos recuperá-lo por meio de um recover() executado na função main() ou em outra goroutine.


O tratamento precisa acontecer dentro da goroutine onde o problema ocorreu.



Quando usar panic e recover?​


O tratamento normal de erros deve continuar sendo feito com error e if err != nil, porque isso permite que o programa identifique problemas e decida como continuar sem interromper inesperadamente sua execução. O panic deve ser reservado para situações excepcionais, como estados inválidos que impedem uma função de continuar corretamente ou falhas que indicam um problema na lógica do programa.


Já o recover pode ser útil quando precisamos impedir que uma falha inesperada encerre todo o programa, como em sistemas que executam tarefas independentes e precisam registrar o problema antes de encerrar a tarefa que falhou. Porém, recuperar um panic não significa que o problema foi resolvido. O programa pode continuar com dados incompletos ou em um estado inválido, dependendo do que aconteceu antes da falha.


Por isso, panic e recover não substituem o tratamento de erros convencional, sendo recursos reservados para situações em que realmente precisamos interromper ou recuperar a execução de uma operação.