自己言及器官

プログラマーワナビー

Goのエラー処理について

TLでまたGoのエラー処理関係で荒れていたので、そういえばちゃんとテストコード書いてみたりして自分の中で整理しなきゃなぁと思ったので書いてみる。

通常の処理

file, err := os.Open(name)  
if err != nil {  
    // Error Handling
}  

もしくは

if file, err := os.Open(name);err != nil {
    // Error Handling
}

というように書く。 エラーを意図的に無視するなら

file, _ := os.Open(name)

というようにエラーの返り値を"_"にバインドする。 基本的にGoではエラーは関数の返り値の一つとして戻ってきて、try-catch-finally構文はそもそも使えない。
こういう風になった理由としてはgoroutineで非同期に処理が行われることと関係があるようだ。
(他にchannelが閉じているかどうかを安全に判別する関数が無い等)
参考:Goでchannelがcloseしてるかどうか知りたい というアンチパターン

recoverを使ったハンドリング

どうしても似た形のパターンを書きたいなら、builtinにある関数のrecoverを使うという手がある。
recoverは基本的に遅延ステートメントのdeferの中で行うことになる、通常panicを呼び出すと 関数の実行は直ちに停止し、プログラム全体が基本的に停止されます。ただしdeferは例外的に実行され、その中でrecoverが実行されたときはそこから処理が継続する。つまり

defer func() {
    if err := recover();err != nil {
        // Error Handling
    }
}()  
errFunc() // 何らかのpanicを起こす処理

というふうに書ける。つまり異常系を外側で処理することができる、しかしこれはオススメされない。
panicは例外的に直ちにプログラムを終了させなければいけないような状態でのみ使われるべきで、 特にライブラリでは初期化を除いて使用されるべきではないとされている。
Javaのような異常系を特別な制御構造を使ってハンドリングするのと、通常の値として返すのと どちらが優れているかどうかは私にはよくわからないが、いちいちファイルのオープン程度でtry-catch-finallyで囲むのは確かに読みづらくなる原因であるようにも思える。(if err != nil〜と書いてハンドリングする方がマシ)

しかしいちいちif err != nilとやりたくない場合

似たようなエラーを返す関数を複数回連続で呼び出した場合当然以下のようになる。

file1, err := os.Open(name1)
if err != nil {
    // Error Handling
}
file2, err := os.Open(name2)
if err != nil {
    // Error Handling
}
file3, err := os.Open(name3)
if err != nil {
    // Error Handling
}

しかし当然これはあまりにも面倒くさいし、DRY原則に反する。 そこで補助的に関数を使って書くとこういう風に書くことができる

var err error
open := func(name string) *os.File {
    var file *os.File
    if err != nil {
        return nil
    }
    file, err = os.Open(name)
    return file
}
file1 := open("file1.txt")
file2 := open("file2.txt")
file3 := open("file3.txt")
if err != nil {
    // Error Handling
}

外側でerrを定義しているのと、わざわざ関数を作ってそれを呼び出すというのを使っているのがなんとなくもにょるがこれで同じハンドリングは一箇所に纏められる。お試しあれ。
参考:Errors are values

Copyright (C) 2015 tSU-RooT. Unless otherwise noted, the text of this page is Dual licensed under a "Creative Commons Attribution 4.0 International License", OR the "GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts", AND also source code is licensed under a "Creative Commons CC0 License".

このブログの記事は必要である範囲で他の著作物を引用していることがあります。また指摘・修正を受け付けます