• ラップされたエラーとマッチするためにerrorsパッケージのIs関数とAs関数を使用する
    • erros.Isの実装例
package main
 
import (
	"errors"
	"fmt"
	"os"
)
 
func fileChecker(name string) error {
	f, err := os.Open(name)
	if err != nil {
		return fmt.Errorf("in fileChecker: %w", err)
	}
	f.Close()
	return nil
}
 
func main() {
	err := fileChecker("not_here.txt")
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			fmt.Println("That file doesn't exist")
		}
	}
}
  • 独自のメソッドIsを定義することでカスタムエラーのフィールドで比較を行うこともできる
package main
 
import (
	"errors"
	"fmt"
)
 
type StatusErr struct {
	Status  int
	Message string
}
 
func (e StatusErr) Error() string {
	return e.Message
}
 
// Isメソッドを実装
func (e StatusErr) Is(target error) bool {
	t, ok := target.(StatusErr)
	if !ok {
		return false
	}
	return e.Status == t.Status
}
 
func main() {
	err := fmt.Errorf("wrap: %w", StatusErr{Status: 404, Message: "not found"})
 
	target := StatusErr{Status: 404}
	fmt.Println(errors.Is(err, target)) // true
}
  • errors.Asの実装例
    • 第二引数はエラー、もしくは、インタフェースのポインタ以外の場合はパニックを起こす
    • 独自のAsメソッドも定義できるが、リフレクションが必要になるため一般的でない状況のみで行うべき
package main
 
import (
	"errors"
	"fmt"
)
 
type MyErr struct {
	Codes []int
}
 
func (me MyErr) CodeVals() []int {
	return me.Codes
}
 
func (me MyErr) Error() string {
	return fmt.Sprintf("codes: %v", me.Codes)
}
 
func AFunctionThatReturnsAnError() error {
	return MyErr{Codes: []int{1, 1, 2, 3, 5, 8}}
}
 
func main() {
	err := AFunctionThatReturnsAnError()
	var myErr MyErr
	if errors.As(err, &myErr) {
		fmt.Println(myErr.Codes) // [1 1 2 3 5 8]
	}
 
	var coder interface {
		CodeVals() []int
	}
	if errors.As(err, &coder) {
		fmt.Println(coder.CodeVals()) // [1 1 2 3 5 8]
	}
}