Pular para o conteúdo principal

victorstein.dev

73/100 Dias de Golang - Panic, Defer e Recover

Table of Contents

# Panic, Defer e Recover

Estava buscando temas e coisas ainda não abordadas. E vi que ainda não falei especificamente sobre Panic, Defer e Recover. Hoje vamos falar mais osbre o Defer.

# Defer

O defer é muito simples, ele agenda um execução para logo antes do return da atual função. Veja esse exemplo:

func deferExemplo() {
    fmt.Println("Início")
    defer fmt.Println("Fim")
    fmt.Println("Meio")
}

A saída será:

Início
Meio
Fim

Uma coisa interessante é que podemos ter vários defer e eles usam o padrão LIFO (last in, first out)

func deferExemplo() {
	fmt.Println("Início")
	defer fmt.Println("Primeiro defer")
	defer fmt.Println("Segundo defer")
	defer fmt.Println("Terceiro defer")
	fmt.Println("Meio")
}
Início
Meio
Terceiro defer
Segundo defer
Primeiro defer

Em Go, podemos dar nome aos valores de retorno diretamente na assinatura da função. Isso faz com que esses valores sejam tratados como variáveis declaráveis e acessíveis dentro da função, sem precisar declará-los novamente. Nessa função de exemplo o return já sabe que deve retornar o resultado

func soma(a, b int) (resultado int) {
	resultado = a + b
	return 
}

E esse comportamento permite com que consigamos alterar o returno do defer:

func teste() (res int) {
	defer func() {
		res += 1
	}()
	return 10
}

Esse ponto é muito importante: O defer é executado depois da avaliação do return, mas antes da saída real da função. Veja essa função: aqui o valor 1 já foi “salvo”; o defer não afeta o retorno

func exemplo() int {
	res := 1
	defer func() {
		res++
	}()
	return res
}

Então temos que o defer é executado após o return ser preparado, mas antes da função realmente retornar o valor.

O Defer é executado mesmo no caso de panic da função:

func exemplo() {
	defer fmt.Println("isso será impresso mesmo com panic")
	panic("erro fatal")
}

Podemos dar um recover dentro de um defer

func exemplo() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recuperado de:", r)
		}
	}()
	panic("algo deu errado")
}

func main() {
	exemplo()
	print("Tudo certo...")

}

A saída:

Recuperado de: algo deu errado
Tudo certo...

O uso do defer está muito associado com a liberação de recursos. Exemplo: fechar um arquivo mesmo se ocorrer erro no meio

file, _ := os.Open("arquivo.txt")
defer file.Close() 

Ou quando estamos usando um Mutex:

var mu sync.Mutex

func acesso() {
	mu.Lock()
	defer mu.Unlock() 
}