エラー

エラー #

エラーを出す #

Go 言語のエラーは error インターフェイスを実装する型として表される。

error インターフェイスは下記の通り Error メソッドを持つだけのものなので、手軽に定義して利用できる。

type error interface {
	Error() string
}

組み込み関数でよく利用されるエラーの初期化関数は errors.Newfmt.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")
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: errors , 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"}
}
play_circleRun open_in_newRun In The Playground

エラーをラップする #

受け取ったエラーを 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")
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: 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
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: errors , fmt

受け取ったエラーがある型のエラーであるかを検証する #

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
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: errors , fmt