Práticas recomendadas da Go - Tratamento de erros

Este é o primeiro artigo de uma série de lições que aprendi nos dois anos em que trabalhei com a Go na produção. Estamos executando um bom número de serviços Go em produção na Saltside Technologies (psst, estou contratando vários cargos em Bangalore para Saltside) e também gerindo meu próprio negócio, onde Go é parte integrante.

Abordaremos uma ampla gama de assuntos, grandes e pequenos.

O primeiro assunto que eu queria abordar nesta série é o tratamento de erros. Isso geralmente causa confusão e aborrecimento para os novos desenvolvedores do Go.

Alguns antecedentes - A interface de erro

Só para estarmos na mesma página. Como você deve saber, um erro no Go é simplesmente algo que implementa a interface de erro. É assim que a definição da interface se parece:

digite interface de erro {
    String de erro ()
}

Portanto, qualquer coisa que implemente o método de string Error () pode ser usado como um erro.

Verificando erros

Usando estruturas de erro e verificação de tipo

Quando comecei a escrever Go, muitas vezes fazia comparações de mensagens de erro para ver qual era o tipo de erro (sim, é constrangedor pensar nisso, mas às vezes você precisa olhar para trás para avançar).

Uma abordagem melhor é usar tipos de erro. Assim, você pode (é claro) criar estruturas que implementam a interface de erro e, em seguida, digitar comparação em uma instrução switch.

Aqui está um exemplo de implementação de erro.

tipo ErrZeroDivision struct {
    sequência de mensagens
}
func NewErrZeroDivision (cadeia de mensagens) * ErrZeroDivision {
    return & ErrZeroDivision {
        mensagem: mensagem,
    }
}
func (e * ErrZeroDivision) Error () string {
    return e.message
}

Agora, esse erro pode ser usado assim.

func main () {
    resultado, erro: = dividir (1,0, 0,0)
    se errar! = nulo {
        switch err. (tipo) {
        case * ErrZeroDivision:
            fmt.Println (err.Error ())
        padrão:
            fmt.Println ("O que diabos aconteceu?")
        }
    }
    fmt.Println (resultado)
}
divisão de funções (a, b float64) (float64, erro) {
    se b == 0,0 {
        return 0.0, NewErrZeroDivision ("Não é possível dividir por zero")
    }
    retornar a / b, nulo
}

Aqui está o link do Go Play para ver o exemplo completo. Observe o padrão de erro de comutação (tipo), que possibilita a verificação de diferentes tipos de erro em vez de outra coisa (como comparação de cadeias ou algo semelhante).

Usando o pacote de erros e comparação direta

A abordagem acima pode ser tratada alternativamente usando o pacote de erros. Essa abordagem é recomendável para verificações de erros dentro do pacote em que você precisa de uma representação rápida de erros.

var errNotFound = errors.New ("Item não encontrado")
func main () {
    err: = getItem (123) // Isso lançaria errNotFound
    se errar! = nulo {
        switch err {
        case errNotFound:
            log.Println ("Item solicitado não encontrado")
        padrão:
            log.Println ("Erro desconhecido")
        }
    }
}

Essa abordagem é menos adequada quando você precisa de objetos de erro mais complexos, por exemplo, códigos de erro etc. Nesse caso, você deve criar seu próprio tipo que implemente a interface de erro.

Tratamento imediato de erros

Às vezes me deparo com códigos como o abaixo (mas geralmente com mais fluff ao redor ..):

erro func exemplo1 () {
    err: = chamada1 ()
    retornar errado
}

O ponto aqui é que o erro não é tratado imediatamente. Essa é uma abordagem frágil, pois alguém pode inserir código entre err: = call1 () e o retorno de erro, o que quebraria a intenção, pois isso pode ocultar o primeiro erro. Duas abordagens alternativas:

// Recolhe o retorno e o erro.
erro func exemplo2 () {
    retornar call1 ()
}
// Executa o tratamento explícito de erros logo após a chamada.
erro func exemplo3 () {
    err: = chamada1 ()
    se errar! = nulo {
        retornar errado
    }
    retorno nulo
}

Ambas as abordagens acima estão bem comigo. Eles alcançam a mesma coisa, o que é; se alguém precisar adicionar algo após call1 (), precisará cuidar do tratamento de erros.

Isso é tudo por hoje

Fique ligado no próximo artigo sobre as Melhores Práticas da Go. Vá forte :).

func main () {
    err: = readArticle ("Práticas recomendadas para ir - Tratamento de erros")
    se errar! = nulo {
        ping ("@ sebdah")
    }
}