DEV Community

renanbastos93
renanbastos93

Posted on

Criando meu pacote de erro usando Go

Neste artigo, vamos dar um rolê pela criação de erros usando Golang, ou seja, com sua biblioteca padrão. Depois desse aquecimento, vamos mergulhar no desenvolvimento do nosso próprio pacote.

Se você ainda não está familiarizado ou não manja muito de interfaces em Golang, eu super recomendo dar uma lida nesse artigo antes, porque a coisa vai ficar intensa. Vamos explorar os pacotes padrão e desvendar como podemos usá-los para construir algo incrível. Então, prepara o café e vem comigo nessa jornada pelos segredos dos pacotes em Golang. Bora nessa?


Para começar, vamos explorar duas maneiras simples de criar erros em Golang, utilizando as bibliotecas padrão 'errors' e 'fmt'. Essas são ferramentas poderosas que vão nos permitir adicionar um toque personalizado aos nossos erros e deixar nossa aplicação mais robusta. Vamos lá!

  1. Usando o pacote errors
errors.New("failed operation") // Aqui vai retornar um novo erro com a mensagem `failed operation`
Enter fullscreen mode Exit fullscreen mode
  1. Usando o pacote fmt
fmt.Errorf("failed again") // Aqui vai retornar um novo erro com a mensagem `failed again`
Enter fullscreen mode Exit fullscreen mode

Uma característica fascinante do pacote fmt é a liberdade que ele oferece para personalizar os erros. Além disso, podemos dar um toque especial ao envolvê-los utilizando a referência %w. Vamos dar uma olhada prática nisso com um exemplo:

errOp := fmt.Errorf("failed operation") // Cria um novo erro
errAgain := fmt.Errorf("%w: again", errOp) // Cria um novo erro que obtém como principal o `errOp`
Enter fullscreen mode Exit fullscreen mode

Essa abordagem não apenas permite personalizar os erros, mas também oferece uma maneira inteligente de repassar informações sem a necessidade de fazer log em cada camada do código. Ao herdar o erro anterior e manter a mesma raiz, conseguimos construir um rastreamento completo do erro, proporcionando uma visão clara da jornada do problema. Uma técnica que não só simplifica, mas também enriquece o processo de identificação e resolução de falhas em nosso código.


Agora, acredito que estamos prontos para dar o próximo passo e criar nosso próprio pacote, respeitando a interface error e aproveitando as funcionalidades do pacote padrão error. Bora lá!


Para iniciar essa jornada incrível, vamos criar uma estrutura personalizada. Vamos seguir a ideia de ter um erro com um código e uma mensagem, algo semelhante aos erros que encontramos no dia a dia das APIs REST. Essa abordagem não apenas facilita a identificação e tratamento de erros, mas também adiciona uma camada de clareza ao nosso código. Vamos colocar a mão na massa e ver o que conseguimos construir juntos!

type myError struct {
    code int
    msg  string
}
Enter fullscreen mode Exit fullscreen mode

Agora que temos nossa struct para representar erros personalizados na aplicação, o próximo passo é criar um método que não apenas instancie o erro, mas também retorne uma interface error. Dessa forma, nossa aplicação pode integrar suavemente esses erros personalizados ao fluxo padrão de manipulação de erros em Golang. Vamos criar esse método e garantir que nossos erros sejam facilmente utilizáveis!

func NewError(code int, msg string) error {
    return &myError{
        code: code,
        msg:  msg,
    }
}
Enter fullscreen mode Exit fullscreen mode

Vale destacar um ponto crucial que frequentemente é negligenciado e resulta em retrabalho desnecessário: muitas pessoas cometem o equívoco de retornar diretamente o ponteiro da estrutura na assinatura do método, em vez da interface error. Essa prática pode gerar complicações adicionais durante validações e manipulação de erros.

Para concluir nosso contrato, vamos adicionar um método Error() string. Esse método é essencial, pois atende ao contrato definido pela interface error, garantindo que nossos erros personalizados possam ser facilmente integrados e utilizados em qualquer contexto que espera uma implementação da interface error. Vamos fortalecer a base do nosso pacote, deixando-o ainda mais robusto e eficiente.

func (e myError) Error() string {
    return fmt.Sprintf("%d: %s", e.code, e.msg)
}
Enter fullscreen mode Exit fullscreen mode

Caramba, pessoal, não é que nosso pacote está pronto? E que tal irmos além e testarmos isso na prática? Vamos fazer um teste repassando nosso erro para frente, utilizando a técnica de wrap com outro erro, e em seguida, validar se o tipo do erro foi herdado corretamente. Ah, vai ser legal! Vamos lá, é hora de entrar nesse teste e garantir que nosso pacote está afiado. Go go go!

// Criando um erro bad request
err := NewError(400, "bad request")

// Envelopando o erro not found dentro do bad request
errAgain := fmt.Errorf("%w: %v", err, NewError(404, "not found"))

// output:
// 400: bad request: 404: not found
fmt.Println(errAgain)

// Aqui estamos validando se o errAgain é herdado do bad request
// output
// true
fmt.Println(errors.Is(errAgain, err))
// Aqui a gente remove o errAgain e pega somente o original
// output
// 400: bad request
fmt.Println(errors.Unwrap(errAgain))
Enter fullscreen mode Exit fullscreen mode

Em conclusão, a valorização e aplicação adequada das interfaces nativas do Go, com destaque para a interface error, revelam-se cruciais. Ao respeitar esses padrões, não apenas fortalecemos a consistência do código, mas também habilitamos rastreamentos mais eficientes quando necessários. Essa abordagem não apenas eleva a qualidade do código, mas também promove a reutilização, consolidando a robustez e flexibilidade no desenvolvimento em Go. Vamos adotar esses princípios para aprimorar continuamente nossa prática de programação em Go.

Para ver o código completo, acesse aqui!

Top comments (0)