Hoje vamos falar sobre um tópico crucial em Go, como lidar com erros. Em Go, erros são valores importantes e devem ser tratados com cuidado.
Definindo um error
Primeiramente, em Go, um erro é qualquer valor que implementa a interface error. Esta interface tem um único método: Error() string.
type MyError struct {
Message string
Code int
}
func (e *MyError) Error() string {
return fmt.Sprintf("error: %s, code: %d", e.Message, e.Code)
}
Embora não seja obrigatório, para facilitar a utilização desse tipo de erro que criamos, podemos escrever uma função New que preenche os atributos.
func New(msg string, code int) *MyError {
return &MyError{ msg, code }
}
Outro ponto interessante, é que MyError pode ser utilizado como retorno em uma função que retorna um tipo error.
Retornando error
Uma prática comum é retornar o erro como o último valor de uma função. Isso permite verificar se uma operação foi bem-sucedida ou não. Por exemplo:
func Funcao() (int, error) {
// lógica da função
}
Dentro dessa função, podemos retornar um erro gerado com errors.New, fmt.Errorf, a função New que criamos anteriormente, ou somente propagar algum erro gerado em alguma chamada interna da função.
O único problema de utilizar qualquer uma dessas 3 funções para retornar um erro, é que ele não será facilmente testável. Isso por que, para testá-lo, será necessário validar a mensagem retornada.
No entanto, para erros conhecidos, Go nos fornece outra abordagem, os chamados sentinel errors. Nessa abordagem, utilizamos erros pré-definidos que podem ser usados para comparação. Um exemplo desse tipo de erro é o io.EOF, que indica o final de um arquivo.
Embora essa abordagem tenha esse nome exclusivo, ela nada mais é do que armazenar os erros em uma variável fora da função. Para o tipo de erro que criamos no início, podemos ter algo como:
var (
ErrNameRequired = New("O Campo nome é obrigatório", http.StatusBadRequest)
)
Tratando error
A chave é o tratamento de erros. Não ignore os erros. Sempre verifique se um erro ocorreu e trate-o de maneira apropriada. Isso pode significar logar o erro, retorná-lo, ou até mesmo parar a execução.
err := json.Unmarshal(data, &v)
if err != nil {
// tratar o erro
}
Além dessa forma, Go 1.13 introduziu melhorias no manuseio de erros, como errors.As e errors.Is, facilitando a verificação e a manipulação de tipos específicos de erros.
person, err := CreateEntity(data)
if errors.Is(err, ErrNameRequired) {
// faça alguma coisa
}
A diferença básica entre as duas funções é que, errors.Is verifica a igualdade de erros, incluindo erros encadeados, ou seja, é útil para comparar erros com valores de erro específicos (como os sentinel errors). Já errors.As é usado para verificar se um erro é de um tipo específico e, se for, fornece acesso à sua instância. Com errors.As, você pode extrair informações adicionais do erro, assumindo que ele é do tipo correto.
Em outras palavras, errors.Is é sobre comparar erros, enquanto errors.As é sobre transformar um erro em um tipo mais específico para acessar informações adicionais.
Conclusão
Lembre-se de que o tratamento de erros é uma parte fundamental do design de software em Go. Com práticas cuidadosas, você pode escrever código mais robusto e confiável.
Espero que estas dicas ajudem você a melhorar a forma como lida com erros em seus projetos Go!
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.




