• 複数エラーをラップするにはerrors.Join関数を使用する
package main
 
import (
	"errors"
	"fmt"
)
 
type Person struct {
	FirstName string
	LastName  string
	Age       int
}
 
func ValidatePerson(p Person) error {
	var errs []error
	if len(p.FirstName) == 0 {
		errs = append(errs, errors.New("field FirstName cannot be empty"))
	}
	if len(p.LastName) == 0 {
		errs = append(errs, errors.New("field LastName cannot be empty"))
	}
	if p.Age < 0 {
		errs = append(errs, errors.New("field Age cannot be negative"))
	}
	if len(errs) > 0 {
		return errors.Join(errs...)
	}
	return nil
} 
 
func main() {
	err := ValidatePerson(Person{
		FirstName: "",
		LastName:  "",
		Age:       -1,
	})
	fmt.Println(err)
	// field FirstName cannot be empty
	// field LastName cannot be empty
	// field Age cannot be negative
}
  • 複数ラップされたエラーをサポートするカスタムエラーを作成することができる
    • 複数ラップされたエラー[]errorerrors.Unwrap関数に渡すと必ずnilが返ってきてしまうので誤動作の原因になってしまう。その為、errors.Unwrap関数は直接使用すべきではない
    • 複数ラップされたカスタムエラーや単一ラップされたカスタムエラーの処理をする場合、下記のように対応する
package main
 
import (
	"errors"
	"fmt"
)
 
type Status int
 
type StatusErr struct {
	Status  Status
	Message string
}
 
func (se StatusErr) Error() string {
	return se.Message
}
 
const (
	InvalidLogin Status = iota + 1
	NotFound
)
 
type MyError struct {
	Code   int
	Errors []error
}
 
func (m MyError) Error() string {
	return errors.Join(m.Errors...).Error()
}
 
func (m MyError) Unwrap() []error {
	return m.Errors
}
 
func funcThatReturnsAnError() error {
	return MyError{
		Code: 12,
		Errors: []error{
			StatusErr{
				Status:  NotFound,
				Message: "file Not Found",
			},
			errors.New("a simple string error"),
		},
	}
}
 
func main() {
	var err error
	err = funcThatReturnsAnError()
	switch err := err.(type) {
	case interface{ Unwrap() error }:
		// 単一ラップエラー
		innerErr := err.Unwrap()
		fmt.Println(innerErr)
	case interface{ Unwrap() []error }:
		// 複数ラップエラー
		innerErrs := err.Unwrap()
		for _, innerErr := range innerErrs {
			fmt.Println(innerErr)
			// file Not Found
   		// a simple string error
		}
	default:
		// デフォルト処理
	}
}