A Tour of Goの練習問題を解説するシリーズ(6/11) – Exercise: Errors

みなさん、こんにちは。人類をGopherにしたいと考えているまるりんです。

A Tour of Goはプログラミング言語Goの入門サイトです。 このシリーズではA Tour of Goの練習問題を解説します。

今回は以下の問題を扱います。

問題
Exercise: Errors
解答
https://go.dev/play/p/lrgdnOYm85X


問題に以下のように書かれているため、これらを脳死で記述します。

type ErrNegativeSqrt float64

ErrNegativeSqrt(-2).Error() で、 "cannot Sqrt negative number: -2" を返すような

func (e ErrNegativeSqrt) Error() string

最初に fmt.Sprint(float64(e)) として e を変換しておくことで、これを避けることができます。

負の値が与えられたとき、 ErrNegativeSqrt の値を返すように Sqrt 関数を修正してみてください。

ソース
https://go.dev/play/p/wlQPq5iPVCo

実行結果

./prog.go:11:37: cannot use ErrNegativeSqrt(x).Error() (type string) as type error in return argument:
    string does not implement error (missing Error method)

コンパイルエラーです。よくみたらerrorはインタフェースでした。

error interface {
    Error() string
}

ErrNegativeSqrtError()を実装しているため、この型でキャストした値を返せば良いことが分かります。 つまり、型ErrNegativeSqrtError()を実装(インタフェースを満た)しているので実体をインタフェースerrorに代入可能です。

ソース
https://go.dev/play/p/_2UYgLReXVr

実行結果

0 <nil>
-2 cannot Sqrt negative number: -2

入力が負の値の場合Error()関数を通るようになりました。 このプログラムに以前作ったSqrt()関数を移植すればプログラム完成です。 型変換したeの値を直接fmt.Sprintf()に渡すように変更しました。

ソース
https://go.dev/play/p/lrgdnOYm85X

実行結果

1.414213562373095 <nil>
-2 cannot Sqrt negative number: -2

最後に問題文の注意にある「なぜでしょうか?」に答えます。

注意: Error メソッドの中で、 fmt.Sprint(e) を呼び出すことは、無限ループのプログラムになることでしょう。 最初に fmt.Sprint(float64(e)) として e を変換しておくことで、これを避けることができます。 なぜでしょうか?

前ページに以下のように書かれています。

fmt.Stringer と同様に、 fmt パッケージは、変数を文字列で出力する際に error インタフェースを確認します。

変数eの型はErrNegativeSqrtです。型ErrNegativeSqrtはerrorインタフェースを満たす関数Error()を実装しています。 ErrNegativeSqrt.Error()fmt.Sprint(e)を呼び出すと、fmt.Sprint()処理内で引数eのe.Error()を呼び出します。このe.Error()ErrNegativeSqrt.Error()です。そのため再びfmt.Sprint(e)が評価されます。 このようにErrNegativeSqrt.Error()の呼び出しが無限に続いてしまいます。これを避けるためにfloat64(e)を使用してeの型をErrNegativeSqrtからfloat64に変換しています。

インタフェースに対する理解がさらに深まりました。