Desafio Go

Agregando erros com errors.Join

Enunciado

Crie uma função validarUsuario(nome, email string) error que valida nome e email. Se ambos forem inválidos, retorne um erro combinado usando errors.Join. Defina erros sentinela ErrNomeVazio e ErrEmailInvalido. No main, chame a função com dados inválidos, verifique com errors.Is se ambos os erros estão presentes e imprima a mensagem combinada.

Requisitos

  • Definir os erros sentinela ErrNomeVazio e ErrEmailInvalido.
  • A função validarUsuario retorna errors.Join dos erros encontrados.
  • No main, usar errors.Is para verificar cada erro individualmente.
  • Imprimir a mensagem do erro combinado.

Código inicial

package main

import (
	"errors"
	"fmt"
)

// Defina erros sentinela

func validarUsuario(nome, email string) error {
	// Implemente
	return nil
}

func main() {
	// Chame validarUsuario("", "invalido") e trate os erros
}

Saída esperada

Erro de nome: nome não pode ser vazio
Erro de email: email inválido
Erro combinado: nome não pode ser vazio
email inválido
Ver dica

Use errors.Join para combinar os erros. No main, verifique cada sentinela com errors.Is e imprima a mensagem do erro combinado.

Mostrar solução
package main

import (
	"errors"
	"fmt"
)

var (
	ErrNomeVazio    = errors.New("nome não pode ser vazio")
	ErrEmailInvalido = errors.New("email inválido")
)

func validarUsuario(nome, email string) error {
	var errs []error
	if nome == "" {
		errs = append(errs, ErrNomeVazio)
	}
	if email == "invalido" {
		errs = append(errs, ErrEmailInvalido)
	}
	return errors.Join(errs...)
}

func main() {
	err := validarUsuario("", "invalido")
	if errors.Is(err, ErrNomeVazio) {
		fmt.Println("Erro de nome:", ErrNomeVazio)
	}
	if errors.Is(err, ErrEmailInvalido) {
		fmt.Println("Erro de email:", ErrEmailInvalido)
	}
	fmt.Println("Erro combinado:", err)
}

Passo a passo

  1. Declaramos dois erros sentinela com errors.New.
  2. Na função validarUsuario, criamos um slice errs para acumular os erros.
  3. Se nome for vazio, adicionamos ErrNomeVazio ao slice.
  4. Se email for "invalido", adicionamos ErrEmailInvalido.
  5. Retornamos errors.Join(errs...), que combina todos os erros em um único erro.
  6. No main, chamamos a função com dados inválidos.
  7. Usamos errors.Is para verificar cada erro sentinela individualmente. errors.Is percorre a árvore de erros combinados.
  8. Imprimimos a mensagem do erro combinado, que contém as mensagens separadas por nova linha.

Por que funciona

errors.Join cria um erro que contém múltiplos erros. Ele implementa Unwrap() []error, permitindo que errors.Is e errors.As percorram todos os erros combinados. Isso é útil quando várias validações falham e você quer reportar todas de uma vez.

Erros comuns

  • Passar um slice vazio para errors.Join: retorna nil, o que pode ser confundido com sucesso. Verifique se há erros antes de chamar.
  • Usar errors.Is com o erro combinado e esperar que ele seja igual a um dos erros: errors.Is verifica se algum erro na cadeia é igual ao alvo, então funciona.
  • Esquecer de importar errors: erro de compilação.
  • Tentar usar == para comparar o erro combinado com um sentinela: não funciona, pois são valores diferentes. Use errors.Is.

Outra forma de resolver

Poderia retornar uma struct customizada que contém um slice de erros e implementa Error() e Unwrap() []error. Mas errors.Join já faz isso de forma padrão e é mais simples.

Saída esperada

Erro de nome: nome não pode ser vazio
Erro de email: email inválido
Erro combinado: nome não pode ser vazio
email inválido