error型
errorは組み込みインターフェースです。
type error interface {
Error() string
}エラーは値
Goでは失敗を例外で飛ばさず、通常は戻り値のerrorとして返します。呼び出し側は毎回if err != nilで分岐し、成功パスと失敗パスを明示的に書きます。
これは制御フローの一部です。エラーを無視するには_で捨てる必要があり、コンパイラは未使用のerrorを警告しませんが、チェックを省略すると失敗が伝播しません。errorを無視して後続の値(nilポインタ、ゼロ値など)を使い続けると、別の場所でパニックやデータ破損につながることがあります。
f, _ := os.Open("missing.txt")
var buf [10]byte
_, _ = f.Read(buf) // 開け失敗時fはnil → panic呼び出し側は取得直後にerrをチェックします。
f, err := os.Open("file.txt")
if err != nil {
return err
}
// ここに来た時点でfは開けているエラーの生成
エラー値はerrors.Newやfmt.Errorfで作ります。
固定の文言だけで足りるときはerrors.Newを使います。
return errors.New("not found")パスや件数など、値をメッセージに埋め込むとき、または下位のerrorに文脈を足して返すときはfmt.Errorfを使います。下位のerrorを包むときは%wを使います。単に文字として並べるだけなら%vです。
return fmt.Errorf("open %s: %w", path, err) // errを包む(呼び出し側で取り出せる)
return fmt.Errorf("open %s: %v", path, err) // errを文字列として埋め込むだけ
return fmt.Errorf("%d items left", n)呼び出し側では、errors.Isで元のエラーかどうかを調べられます。%wで包んだときだけ一致し、%vではメッセージに文字が入るだけで元のerrorとしては取り出せません。
var ErrNotFound = errors.New("not found")
wrapped := fmt.Errorf("load config: %w", ErrNotFound)
fmt.Println(errors.Is(wrapped, ErrNotFound)) // true
textOnly := fmt.Errorf("load config: %v", ErrNotFound)
fmt.Println(errors.Is(textOnly, ErrNotFound)) // falsenil errorは成功
errorのゼロ値はnilです。err == nilのとき処理は成功したとみなします。非nilのerror値が返ったとき、呼び出し側は失敗として扱います。
func mayFail(ok bool) error {
if ok {
return nil // 成功
}
return errors.New("failed")
}
if err := mayFail(true); err != nil {
// 失敗
} else {
// err == nilなら成功
}if err != nilの位置
エラーは直後にチェックするのが慣習です。後続のコードが成功を前提に書けるようになります。
panic
panicは実行時に異常を報告し、通常はそのゴルーチンの実行を中断します。
ライブラリの公開APIでは、呼び出し側が予期しうる失敗(ファイルが無い、形式が不正など)はerrorで返します。panicはプログラムのバグ、不変条件の破れ、回復不能な内部状態向けです。予期可能な失敗をpanicにすると、呼び出し側が通常のerrorチェックで扱えず、APIの契約が一貫しなくなります。
if p == nil {
panic("nil pointer") // バグの検出向け
}recover
recoverはpanicによって中断された実行を再開します。deferされた関数の中でのみ有効です。recoverは最後の防衛線であり、ループや分岐の代わりに使うと読みにくく、想定外のpanicも飲み込んでしまいます。
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()recoverのスコープ
recoverが効くのは、同じゴルーチン内でpanicしたときだけです。別ゴルーチンで起きたpanicは、そのゴルーチンを終了させ、回復されなければプログラム全体が終了します。呼び出し元のdefer内のrecoverは、別ゴルーチンで起きたpanicを捕まえられません。