Go言語(golang) エラーと独自エラーの実装
エラーは errors パッケージや fmt パッケージを使ってエラーを作成します。
それとは別で、errorインターフェースの要件を満たせば独自のエラーを作成できます。
エラーの作成
- fmt.Errorf は fmt.Sprintf のようにフォーマットできる
- errors.New 文字列のみでエラーを作成する
package main import ( "errors" "fmt" ) func main() { { err := fmt.Errorf("%d: %s", 1, "Some error") println(err.Error()) // 1: Some error } { err := errors.New("Some error") println(err.Error()) // Some error } }
エラーにファイル名や行番号を付加する
package main import ( "fmt" "runtime" ) func main() { _, file, line, ok := runtime.Caller(0) if ok { err := fmt.Errorf("Some error - file: %s, line: %d", file, line) println(err.Error()) // Some error - file: main.go, line: 10 } }
独自エラーの作成方法
標準パッケージのコードを見ていると csv/reader.go など、独自のエラーが定義してあります。
ビルトイン error は以下のようにインターフェイスで定義されているので、
構造体にError関数をもたせれば独自のエラーを作ることができます。
type error interface { Error() string }
独自エラーの実装と errorsパッケージ(New,Unwrap,Is,As)の使い方
package main import ( "errors" "fmt" ) // なんのエラーなのか、比較、確認するために定義しておく // 慣例として Err を変数名の先頭につける // errors.New は type errorString struct を返す https://golang.org/src/errors/errors.go?s=1875:1902#L48 var ( ErrOne = errors.New("error 1") ErrTwo = errors.New("error 2") ErrUnknown = errors.New("error unknown") ) type CustomError struct { // エラーが発生した行番号などを持たせておく N int Err error } // fmt.Println など、出力するための関数は、内部的に error型だった場合に Error を使う // 行番号など、エラーメッセージに付け足すための処理を書いておく func (e *CustomError) Error() string { if e.Err == ErrOne { return fmt.Sprintf("error number %d: %v", e.N, e.Err) } if e.Err == ErrTwo { return fmt.Sprintf("error number %d: %v", e.N, e.Err) } return e.Err.Error() } // Unwrap を定義しておけば、errors.Unwrap が使える func (e *CustomError) Unwrap() error { return e.Err } // エラーを返すだけの関数 func ReturnsAnError(n int) error { switch n { case 1: // 必ず & でポインターとして返す return &CustomError{N: 1, Err: ErrOne} case 2: return &CustomError{N: 2, Err: ErrTwo} default: return &CustomError{N: -1, Err: ErrUnknown} } } func main() { err := ReturnsAnError(1) // エラーを型変換して、なんのエラーなのか比較して特定する if e, ok := err.(*CustomError); ok { switch e.Err { case ErrOne: fmt.Printf("N: %d, %v\n", e.N, err) case ErrTwo: fmt.Printf("N: %d, %v\n", e.N, err) default: fmt.Printf("N: %d, %v\n", e.N, err) } } // errors.Unwrap で、error だけを取り出す。 // 独自エラーにUnwrap関数が定義されていなければ nil が返る if e := errors.Unwrap(err); e != nil { fmt.Println(e) } // errors.As は _, ok := err.(*CustomError) と同じ var ce *CustomError fmt.Println("errors.As:", errors.As(err, &ce)) // true // errors.Is は e.Err == ErrOne と同じ if e, ok := err.(*CustomError); ok { fmt.Println("errors.Is:", errors.Is(e.Err, ErrOne)) } }