Desafio Go

Erros personalizados com errors.As

Enunciado

Implemente um tipo de erro personalizado ErroValidacao com campos Campo e Mensagem. Crie uma função ValidarUsuario(nome string, idade int) error que retorna um *ErroValidacao se nome estiver vazio ou idade for menor que 18. A função deve retornar o primeiro erro encontrado. No main, chame ValidarUsuario("", 20) e use errors.As para extrair o *ErroValidacao e imprimir "Erro no campo: nome - mensagem: nome não pode ser vazio". Depois, chame ValidarUsuario("João", 15) e faça o mesmo para o campo idade.

Requisitos

  • Definir a struct ErroValidacao com campos Campo e Mensagem e método Error() string.
  • A função ValidarUsuario retorna *ErroValidacao para nome vazio ou idade < 18.
  • No main, usar errors.As para extrair o erro e imprimir a mensagem formatada.
  • Imprimir exatamente "Erro no campo: nome - mensagem: nome não pode ser vazio" e "Erro no campo: idade - mensagem: idade mínima é 18".

Código inicial

package main

import (
	"errors"
	"fmt"
)

type ErroValidacao struct {
	Campo    string
	Mensagem string
}

func (e *ErroValidacao) Error() string {
	return fmt.Sprintf("erro no campo %s: %s", e.Campo, e.Mensagem)
}

func ValidarUsuario(nome string, idade int) error {
	// Implemente
	return nil
}

func main() {
	// Chame ValidarUsuario e use errors.As
}

Saída esperada

Erro no campo: nome - mensagem: nome não pode ser vazio
Erro no campo: idade - mensagem: idade mínima é 18
Ver dica

Use var valErr *ErroValidacao e errors.As(err, &valErr). Depois acesse valErr.Campo e valErr.Mensagem.

Mostrar solução
package main

import (
	"errors"
	"fmt"
)

// ErroValidacao representa um erro de validação com campo e mensagem.
type ErroValidacao struct {
	Campo    string
	Mensagem string
}

// Error implementa a interface error.
func (e *ErroValidacao) Error() string {
	return fmt.Sprintf("erro no campo %s: %s", e.Campo, e.Mensagem)
}

// ValidarUsuario retorna um *ErroValidacao se nome for vazio ou idade < 18.
func ValidarUsuario(nome string, idade int) error {
	if nome == "" {
		return &ErroValidacao{Campo: "nome", Mensagem: "nome não pode ser vazio"}
	}
	if idade < 18 {
		return &ErroValidacao{Campo: "idade", Mensagem: "idade mínima é 18"}
	}
	return nil
}

func main() {
	// Teste 1: nome vazio
	err := ValidarUsuario("", 20)
	if err != nil {
		var valErr *ErroValidacao
		if errors.As(err, &valErr) {
			fmt.Printf("Erro no campo: %s - mensagem: %s\n", valErr.Campo, valErr.Mensagem)
		}
	}

	// Teste 2: idade inválida
	err = ValidarUsuario("João", 15)
	if err != nil {
		var valErr *ErroValidacao
		if errors.As(err, &valErr) {
			fmt.Printf("Erro no campo: %s - mensagem: %s\n", valErr.Campo, valErr.Mensagem)
		}
	}
}

Passo a passo

  1. Definimos a struct ErroValidacao com os campos Campo e Mensagem.
  2. Implementamos o método Error() string para que *ErroValidacao satisfaça a interface error.
  3. A função ValidarUsuario verifica primeiro se nome é vazio; se for, retorna um *ErroValidacao com os detalhes.
  4. Se idade < 18, retorna outro *ErroValidacao com os detalhes da idade.
  5. No main, para cada chamada, usamos errors.As com uma variável do tipo *ErroValidacao para extrair o erro concreto da cadeia.
  6. Se a extração for bem-sucedida, acessamos os campos e imprimimos a mensagem formatada.

Por que funciona

errors.As percorre a cadeia de erros verificando se algum erro é do tipo alvo (no caso, *ErroValidacao). Se encontrar, atribui o valor à variável fornecida e retorna true. Isso permite acessar campos específicos do erro personalizado, mesmo que ele esteja envolto por outros erros.

Erros comuns

  • Passar &valErr como *ErroValidacao em vez de **ErroValidacao: errors.As espera um ponteiro para a variável que receberá o erro. O correto é var valErr *ErroValidacao; errors.As(err, &valErr).
  • Retornar ErroValidacao (valor) em vez de *ErroValidacao: se o método Error() tem receptor ponteiro, apenas *ErroValidacao implementa error. Retornar valor causaria erro de compilação.
  • Esquecer de verificar o retorno de errors.As: se o tipo não corresponder, a variável permanece nil e um panic pode ocorrer ao acessar campos.

Outra forma de resolver

Se você precisa apenas da mensagem, pode usar err.Error(). Mas para acessar campos específicos, errors.As é a ferramenta correta.

Saída esperada

Erro no campo: nome - mensagem: nome não pode ser vazio
Erro no campo: idade - mensagem: idade mínima é 18