Go言語のエラー処理の仕組み② - 比較編
前回は、エラー処理の基本的な所を解説しました。
前回の記事:Go言語のエラー処理の仕組み① - 基本編
今回はエラー変数の比較について解説します。
エラー変数の比較
エラー変数の比較なんて、==
演算子を使えばいいのでは?と思う方がいらっしゃるかも知れません。
たしかに下記のようなケースであれば==
演算子でも問題なく比較できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var ErrIsNotMikeNeko = errors.New("みけ猫ではありません!")
func IsMikeNeko(s string) error {
if s == "みけ猫" {
return nil
} else {
return ErrIsNotMikeNeko
}
}
func main() {
err := IsMikeNeko("ぶち猫")
if err == ErrIsNotMikeNeko {
fmt.Println(err)
}
}
|
この場合はErrIsNotMikeNeko変数が直接返却されているため==
演算子で比較しても問題はありません。
しかし、次のように構造体の中にエラー変数がある場合はどうでしょうか。
1
2
3
4
|
type NekoCheckWrapError struct {
ErrorString string
Err error
}
|
同じようにエラー変数を作成して==
演算子で比較してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
var ErrIsNotMikeNeko = errors.New("みけ猫ではありません!")
type NekoCheckWrapError struct {
ErrorString string
Err error
}
func (e *NekoCheckWrapError) Error() string {
return fmt.Sprintf("%s:%v", e.ErrorString, e.Err)
}
func IsMikeNekoWrap(s string) error {
if s == "みけ猫" {
return nil
} else {
return &NekoCheckWrapError{
ErrorString: s,
Err: ErrIsNotMikeNeko,
}
}
}
func main() {
err = IsMikeNekoWrap("ぶち猫")
if err == ErrIsNotMikeNeko {
fmt.Println(err)
} else {
fmt.Println("ErrIsNotMikeNekoと一致しません!")
}
}
|
1
|
ErrIsNotMikeNekoと一致しません!
|
当然ながら==
演算子では構造体内部のエラー変数のみと比較するわけではないので、一致しません。
このようにGo言語では構造体内部にエラー変数がある場合、エラー変数がラップされている状態であると表現します。
このラップされた状態のエラー変数に対して比較を行うために、errors.Isという関数が用意されています。
errors.Is関数を使用してエラー変数を比較する
ではerrors.Is関数を使って比較してみましょう。
1
2
3
4
5
6
7
8
|
func main() {
err = IsMikeNekoWrap("ぶち猫")
if errors.Is(err, ErrIsNotMikeNeko) {
fmt.Println(err)
} else {
fmt.Println("ErrIsNotMikeNekoと一致しません!")
}
}
|
1
|
ErrIsNotMikeNekoと一致しません!
|
おや…?errors.Is関数を使ってもうまく比較できていませんね🤔
これは残念ながら、NekoCheckWrapError変数にUnwrapメソッドが実装されていないため比較できないのです。
エラー変数にUnwrapメソッドを実装する
Unwrapメソッドは、構造体内部のエラー変数のみを返却する役割を持っています。
1
2
3
|
func (e *NekoCheckWrapError) Unwrap() error {
return e.Err
}
|
errors.Is関数は、内部でこのUnwrapメソッドを呼び出してエラー変数と比較を行っています。
では、Unwrapメソッドを実装して比較してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
|
func (e *NekoCheckWrapError) Unwrap() error {
return e.Err
}
func main() {
err = IsMikeNekoWrap("ぶち猫")
if errors.Is(err, ErrIsNotMikeNeko) {
fmt.Println(err)
} else {
fmt.Println("ErrIsNotMikeNekoと一致しません!")
}
}
|
今度はちゃんと比較できました🤗
このようにGo言語では、一貫した方法でエラー変数の比較ができるようにerrors.Is関数が用意されています。
そのため、「構造体内部にエラー変数を持つ場合はUnwrapメソッドを実装しましょう」という規約がGo 1.13のバージョンから追加されました。(errors.Is関数もGo 1.13から追加されています)
このエラー変数のラップは標準パッケージでよく使われている手法なので、特にここの事情を理解していないと、比較でつまづいてしまう方がいらっしゃると思います🙄
まぁ私のことなんですが😇
最後に
今回は比較編を書いてみましたが、いかがでしたか。
Go言語ではエラー処理周りで色々な約束事があるため、比較一つとっても中々とっつきにくいです。
しかし、このような決まり事が分かってくるとGo言語でのプログラミングがもっと楽しくなるので、積極的に学んでいきましょう。
プログラム全体
今回の解説で使用したすべてのコードをのせています。
コピーしてGo Playground
に貼り付ければ実行できますので、実際の動作を確認してみてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package main
import (
"errors"
"fmt"
)
var ErrIsNotMikeNeko = errors.New("みけ猫ではありません!")
type NekoCheckWrapError struct {
ErrorString string
Err error
}
func (e *NekoCheckWrapError) Error() string {
return fmt.Sprintf("%sは%v", e.ErrorString, e.Err)
}
func (e *NekoCheckWrapError) Unwrap() error {
return e.Err
}
func IsMikeNeko(s string) error {
if s == "みけ猫" {
return nil
} else {
return ErrIsNotMikeNeko
}
}
func IsMikeNekoWrap(s string) error {
if s == "みけ猫" {
return nil
} else {
return &NekoCheckWrapError{
ErrorString: s,
Err: ErrIsNotMikeNeko,
}
}
}
func main() {
//-----------------------------------------------------------
//エラー変数の比較
//-----------------------------------------------------------
err := IsMikeNeko("ぶち猫")
if err == ErrIsNotMikeNeko {
fmt.Println(err)
}
err = IsMikeNekoWrap("ぶち猫")
if err == ErrIsNotMikeNeko {
fmt.Println(err)
} else {
fmt.Println("ErrIsNotMikeNekoと一致しません!")
}
//-----------------------------------------------------------
//errors.Is関数を使用してエラー変数を比較する
//-----------------------------------------------------------
err = IsMikeNekoWrap("ぶち猫")
if errors.Is(err, ErrIsNotMikeNeko) {
fmt.Println(err)
} else {
fmt.Println("ErrIsNotMikeNekoと一致しません!")
}
}
|
1
2
3
|
みけ猫ではありません!
ErrIsNotMikeNekoと一致しません!
ぶち猫はみけ猫ではありません!
|
実行環境
1
2
|
~/$ go version
go version go1.18 linux/amd64
|