defer, panic & recover
defer agenda a limpeza na saída da função, panic inicia o desenrolar da pilha e recover pode interromper esse desenrolar - mas apenas dentro de uma chamada deferida.
Busque em todas as páginas da documentação
defer agenda a limpeza na saída da função, panic inicia o desenrolar da pilha e recover pode interromper esse desenrolar - mas apenas dentro de uma chamada deferida.
Esses três mecanismos são a resposta do Go para a desmontagem estruturada sem exceções para erros normais.
defer empilha chamadas em uma pilha LIFO por goroutine executada quando a função envolvente retorna.
Argumentos para funções deferidas são avaliados imediatamente, mas a chamada espera até o retorno.
panic executa chamadas deferidas enquanto procura por um recover.
recover retorna o valor do panic apenas durante esse desenrolar.
Falhas esperadas devem usar retornos error, não panic.
Cartão de receita de referência rápida - pronto para copiar e colar.
package main
import (
"fmt"
"os"
)
func readConfig(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
// Agenda o fechamento do arquivo para quando a função readConfig retornar.
defer f.Close()
buf := make([]byte, 64)
n, err := f.Read(buf)
if err != nil {
return "", err
}
return string(buf[:n]), nil
}
func main() {
text, err := readConfig("config.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(text)
}Quando usar isso:
defer imediatamente após a aquisição bem-sucedida de um recurso (Open, Lock, BeginTx).panic para estados impossíveis que indicam bugs do programador, não falhas de I/O.recover em limites de processo (middleware HTTP, nível superior de worker) para registrar e retornar 500.error para qualquer coisa que um chamador possa razoavelmente lidar ou tentar novamente.package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
// Agenda a decrementação do WaitGroup para quando a função worker retornar.
defer wg.Done()
// Agenda uma função anônima que tentará recuperar de um panic.
defer func() {
if r := recover(); r != nil {
fmt.Printf("worker %d recovered: %v\n", id, r)
}
}()
if id == 2 {
// Simula um bug causando um panic.
panic("simulated bug")
}
fmt.Println("worker", id, "ok")
}
func main() {
var wg sync.WaitGroup
for id := 1; id <= 3; id++ {
wg.Add(1)
go worker(id, &wg)
}
wg.Wait()
fmt.Println("all workers finished")
}O que isso demonstra:
defer wg.Done() é executado mesmo quando panic ocorre mais tarde na função.defer interno com recover captura o panic sem encerrar todo o programa.main.defer.defer, não no momento do desenrolar (cuidado com variáveis de loop e ponteiros).panic, o runtime executa as funções deferidas de dentro para fora até que recover pare a propagação ou a pilha termine.recover() fora de uma função deferida sempre retorna nil.| Fase | O que acontece |
|---|---|
defer f() | Empilha f na pilha de defer; avalia os argumentos agora |
| Retorno normal | Executa todos os defers LIFO, depois retorna ao chamador |
panic(v) | Inicia o desenrolar; executa defers LIFO nesta goroutine |
recover() em defer | Interrompe o desenrolar; retorna v para a função deferida |
| Panic não recuperado | Trava a goroutine / processo com rastreamento de pilha |
// Resultado nomeado + defer pode ajustar valores de retorno
func divide(a, b int) (q int, err error) {
// Agenda a verificação de divisão por zero.
defer func() {
if b == 0 {
// Se b for zero, define o erro.
err = fmt.Errorf("divide by zero")
}
}()
// Cuidado: a divisão ainda pode causar panic por zero nesta forma ingênua.
return a / b, nil
}
// Prefira verificação explícita antes da divisão em vez de confiar no panicfor { defer f() } aumenta a pilha de defer até que a função retorne. Correção: envolva o corpo do loop em uma função aninhada ou use defer fora do loop principal.defer; closures em literais de função deferida veem os valores finais. Correção: passe valores como parâmetros: defer func(v int) { ... }(i).recover() retorna nil e não para o panic. Correção: chame recover apenas dentro de defer func() { ... }().recover vazio esconde bugs. Correção: registre a pilha, incremente métricas, retorne 500 para clientes.error ou um resultado (T, bool).defer para a função externa ou use limpeza explícita quando comprovadamente for um gargalo.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Retorno error | Falhas esperadas | Quebras de invariante verdadeiramente irrecuperáveis |
Limpeza com sync.Once | Limpeza única | Recursos por requisição |
Cancelamento com context.Context | Propagar desligamento | Simples Close() em um único handle |
Padrão try/finally via defer | Sempre precisa de limpeza | Linguagem não tem finally - defer é o idioma |
| Supervisor de processo | Isolar workers que travam | Handlers HTTP em processo (use recover) |
Na instrução defer, não quando a chamada deferida é executada.
Variáveis de loop em argumentos precisam de cópia cuidadosa.
O último deferido é executado primeiro (LIFO).
Espelhe a ordem de aquisição de recursos em ordem inversa para liberação.
Sim - esse é um caso de uso principal para limpeza.
Funções deferidas são executadas enquanto a pilha está sendo desenrolada.
Recover interrompe o desenrolar na goroutine atual.
A execução continua após a função deferida que recuperou, não no local do panic.
Bibliotecas devem retornar erros para falhas operacionais.
Use panic apenas para uso indevido que os chamadores não podem prevenir.
Um novo panic durante o desenrolar pode substituir o valor do panic anterior.
Mantenha as funções deferidas simples e sem panic.
Pequeno custo fixo por defer.
Evite milhares de defers em uma única função - refatore loops.
Sim - defer f.Close() e defer mu.Unlock() são padrão.
O receptor é avaliado com outros argumentos no momento do defer.
Não - cada goroutine se desenrola independentemente.
O middleware recover protege apenas a goroutine do handler que ele envolve.
Use debug.Stack() ou runtime.Stack dentro do bloco de recover deferido.
Inclua IDs de requisição do contexto para serviços HTTP.
Versões de Stack: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026