今川館

都内勤務の地味OLです

Goのエラー処理① エラー処理の基本的な考え方

Goのエラー処理方針

Goの例外やエラーの処理について勉強中。まず簡単なことからメモ。

例外は原則として使わない・予期される異常処理はすべて手当する

プログラミング言語Go』を読むと、Goに例外の仕組みはあるけど、基本的にエラーを丁寧に処理しないという方針らしい。

(前略)Goはある種の例外機構を持っていますが、バグであることを示す本当に予期されていないエラーを報告するためだけに使われ、頑強なプログラムを構築するためのルーチンで予期されるエラーには使われません。

なので、エラーを処理する関数は戻り値にerror型の値を返すよう実装するのが良いようだ。

エラーの作り方

エラーは組み込みのerror型を利用する。作り方は

  • errors.New("エラーメッセージ")で作る
  • fmt.Errorf("...")で作る

上記二通りあるようだ。(以下、サンプル)

package main

import (
	"errors"
	"fmt"
)

func main() {
	var err error

	err = errors.New("処理に失敗しました") // エラーの原因などが何も無いので、あまり良くないメッセージ
	fmt.Println(err)

	err = errors.New(fmt.Sprintf("処理中に接続が切れたためリトライを開始します(%v回め)", 3))
	fmt.Println(err)

	// 前のエラー内容を引き継いで新規エラーメッセージを追記する場合
	err = fmt.Errorf("%v: リトライ回数が一定に達したため、処理を打ち切ります(%v回試しました)", err, 7)
	fmt.Println(err)
}

エラーメッセージはデバッグ等に役立てるためにも、(引数やローカル変数の内容も併記することが望ましいので)、とりあえずfmt.Errorfを使っていれば良いんじゃないかと思った。

エラーメッセージのルール

これも『プログラミング言語Go』で推奨されているエラーメッセージのルールだが、

  • メッセージに大文字を使わない方が良い(頻繁に連鎖[追記]するから)
  • 改行文字も使わない(grepしにくくなるから)
  • 関数f(x)のエラーは、操作fに対する引数xとエラーの関連がわかるように書く
  • サブルーチンから返却されたエラーに無い情報は、呼び出し元で追記して最終的なエラーとして返却する(エラーの伝搬)

PythonJavaなどの例外を常用する言語でプログラムを書いていると、メインルーチンで基底の例外を捕捉し、サブルーチンでより詳しい例外派生クラスを送出する処理を書く手法に慣れているので、Goのルールになじむまでなかなか骨が折れそうだ。

ただ、Goのポリシーは異常が起きた箇所で確実に手当していく心がけにもつながるんじゃないかと思う。