Aggregate Field Errors with errors.Join

Collect every invalid signup field with errors.Join, then inspect the combined error using errors.As and Unwrap.

GoForm Validation

Code

snippet
package main

import (
	"errors"
	"fmt"
	"strings"
)

type FieldError struct {
	Field string
	Rule  string
}

func (err FieldError) Error() string {
	return err.Field + ": " + err.Rule
}

type Signup struct {
	Email string
	Age   int
}

type multiError interface {
	Unwrap() []error
}

func validate(signup Signup) error {
	var problems []error
	if !strings.Contains(signup.Email, "@") {
		problems = append(problems, FieldError{Field: "email", Rule: "must contain @"})
	}
	if signup.Age < 18 {
		problems = append(problems, FieldError{Field: "age", Rule: "must be at least 18"})
	}
	return errors.Join(problems...)
}

func main() {
	err := validate(Signup{Email: "nope", Age: 15})
	if err == nil {
		panic("invalid signup should fail")
	}
	fmt.Println(err)

	var fieldError FieldError
	if !errors.As(err, &fieldError) {
		panic("expected a FieldError in the joined error")
	}
	if fieldError.Field != "email" {
		panic("expected the first field error to be reported")
	}

	combined, ok := err.(multiError)
	if !ok || len(combined.Unwrap()) != 2 {
		panic("expected two joined errors")
	}
	if validate(Signup{Email: "[email protected]", Age: 30}) != nil {
		panic("valid signup should pass")
	}
}