エラー #
エラーを出す #
Go 言語のエラーは error インターフェイスを実装する型として表される。
error インターフェイスは下記の通り Error メソッドを持つだけのものなので、手軽に定義して利用できる。
type error interface {
Error() string
}
組み込み関数でよく利用されるエラーの初期化関数は errors.New
や fmt.Errorf
などである。
package main
import (
"errors"
"fmt"
)
func main() {
if err := func1(); err != nil {
fmt.Println(err)
}
if err := func2(); err != nil {
fmt.Println(err)
}
}
func func1() error {
return errors.New("Hello New Error")
}
func func2() error {
return fmt.Errorf("Hello %s Error", "fmt")
}
独自エラーを定義する #
error インターフェイスを実装する構造体を定義して error として関数の返り値で利用する。
package main
import (
"fmt"
)
type MyError struct {
Message string
}
func (e MyError) Error() string {
return e.Message
}
func main() {
if err := func1(); err != nil {
fmt.Println(err)
}
}
func func1() error {
return MyError{Message: "hello my error"}
}
エラーをラップする #
受け取ったエラーを fmt.Errorf
のフォーマットの %w
で表すことで、エラーをラップして新しいエラーとして返せる。
これは例えばエラーがどのような文脈で発生したかなどの情報をエラーに補足したい場合などに便利である。
package main
import (
"fmt"
)
func main() {
e := func1()
fmt.Println(e)
}
func func1() error {
return fmt.Errorf("func1 error:\n%w", func2())
}
func func2() error {
return fmt.Errorf("func2 error:\n%w", func3())
}
func func3() error {
return fmt.Errorf("func3 error")
}
参考ドキュメント: fmt
受け取ったエラーがある特定のエラーであるかを検証する #
errors
パッケージの func Is(err, target error) bool
を使うと受け取ったエラーが既に定義済みのある特定のエラーと一致するかどうかを検証できる。
err には受け取ったエラーを、target には比較対象のエラー定数を指定する。
エラーはラップされていても検証可能である。
これは例えばパッケージの作成者がエラーを定数として用意しておき、ユーザーがエラーを判別して処理を分岐させる場合などに用いられる。
package main
import (
"errors"
"fmt"
)
type MyError string
const MyError400 MyError = "not found"
const MyError500 MyError = "internal server error"
func (e MyError) Error() string {
return string(e)
}
func main() {
e1 := MyError400
fmt.Println(errors.Is(e1, MyError400)) // == true
fmt.Println(errors.Is(e1, MyError500)) // == false
e2 := MyError500
fmt.Println(errors.Is(e2, MyError400)) // == false
fmt.Println(errors.Is(e2, MyError500)) // == true
e3 := fmt.Errorf("error: %w", MyError400)
fmt.Println(errors.Is(e3, MyError400)) // == true
fmt.Println(errors.Is(e3, MyError500)) // == false
}
受け取ったエラーがある型のエラーであるかを検証する #
errors
パッケージの func As(err error, target interface{}) bool
を使うと受け取ったエラーがある型のエラーであるかどうかを検証できる。
err には受け取ったエラーを、target は比較対象のエラー型の値のポインタを指定する。
エラーはラップされていても検証可能である。
errors.Is
はエラーの値まで一致するかどうかを検証するのに対して、errors.As
は型の一致のみを検証するので、エラーの値に何らかの情報を持たせてエラーを受けた側で利用する場合などに使える。
package main
import (
"errors"
"fmt"
)
type MyError1 struct {
Number int
Message string
}
func (e *MyError1) Error() string {
return fmt.Sprintf("[%d] %s", e.Number, e.Message)
}
type MyError2 string
func (e *MyError2) Error() string {
return string(*e)
}
func main() {
var myError1 *MyError1
var myError2 *MyError2
e1 := &MyError1{Number: 1, Message: "first error"}
fmt.Println(errors.As(e1, &myError1)) // == true
fmt.Println(errors.As(e1, &myError2)) // == false
e2 := MyError2("second error")
fmt.Println(errors.As(&e2, &myError1)) // == false
fmt.Println(errors.As(&e2, &myError2)) // == true
e3 := fmt.Errorf("error: %w", &MyError1{Number: 3, Message: "third error"})
fmt.Println(errors.As(e3, &myError1)) // == true
fmt.Println(errors.As(e3, &myError2)) // == false
}