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.
Desafio
FácilVamos 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
Divideque 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
SafeDivideque receba dois inteiros e retorne tanto um resultado inteiro quanto um erro. Essa função deve:- Usar
defercom 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
Divideinternamente - Retornar o resultado e
nilse for bem-sucedida, ou zero e o erro caso um pânico tenha sido recuperado
- Usar
main.go: Leia dois inteiros da entrada e useSafeDividepara realizar o cálculo com segurança. Demonstre tambémdeferimprimindo mensagens de limpeza na ordem correta.Sua função main deve:
- Imprimir
Starting calculationno início - Usar
deferpara agendar a impressão deCleanup complete - Usar outro
deferpara agendar a impressão deReleasing resources - Chamar
SafeDividecom os valores de entrada - Imprimir
Result: [value]ouError: [error message], dependendo do resultado
- Imprimir
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 completeE dados 10 e 0, sua saída deve ser:
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup completeExperimente você mesmo
package main
import (
"fmt"
)
func main() {
// Ler entrada
var dividend, divisor int
fmt.Scanln(÷nd)
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
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online