LoginSignup
137
62

More than 3 years have passed since last update.

try - Go の新しいエラーハンドリング (Go 1.14で導入予定)(でしたが,却下となりました)

Last updated at Posted at 2019-06-27

Go 1.14 で try というシンプルなエラーハンドリング方法が導入予定です。

2019/07/17 追記
議論の結果、try は一旦却下となりました。詳しくは提案者 Robert Griesemer 氏のコメントを参照下さい。

error

Go でのエラーハンドリングはややタイプ数が多い。

    f, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("...", err)
    }

のようなエラー処理コードを何度も書かないといけないことにストレスを感じることがあるかもしれません。

それで,去年(2018年)の GopherCon で Go 2 のドラフトデザインが発表された際,エラーハンドリングの提案が含まれていました。
checkhandle を使う提案です。

check と handle

    handle err {
        return fmt.Errorf("...", err)
    }

    f := check os.Open(filename)

戻り値の最後が error 型の場合,関数呼び出しの前に check と書けば, error が nil でない場合に handle が実行されるという提案です。
ただ,handledeferの機能が重複しているのではないか,複雑すぎるのではないか等の意見がありました。
この提案が出てから議論が重ねられ,現在,より単純化された try を使ったエラーハンドリングが Go 1.14 で導入予定になっています。

try

func FuncXXX() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("...", err) 
        }
    }()
    f := try(os.Open(filename))
    // ...
    return
}

try を使う条件として,関数の返り値の最後のパラメータは error 型である必要があります。
err は関数の名前付き返り値パラメータです。err 以外の名前でもいいですし,使わないのであれば名前なしでもかまいません。

このコードは,次のコードと同等になります。

func FuncXXX() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("...", err) 
        }
    }()
    t1, te := os.Open(filename)
    if te != nil {
        err = te // te を error型の返り値パラメータに代入する
        return   // try のまわりの関数 (この場合,FuncXXX) から返る 
    }
    f := t1      // エラーが発生しなかった時だけ代入が行われる
    // ...
    return
}

なお,try の導入と同時に fmt パッケージに HandleErrorf というヘルパー関数が入る予定です。
これを使えば, defer 部分は簡潔に,

    defer fmt.HandleErrorf(&err, "...")

のように書けます。

try の利点

check, handle は演算子ですが, try はビルトイン関数であることが大きな違いです。
check, handle に比べ, try にはどんな利点があるのでしょうか?

  • Go のすでにある機能 defer とビルトイン関数を使うので, 言語が複雑化しない。
  • Go に新たな構文を導入しなくてすむ。
  • 完全に後方互換性を保てる。
  • カッコを書く必要があるのはちょっと面倒に見えるかもしれないが,優先順位にあいまいなところがない。
  • try は単なる糖衣構文

簡単で Go 言語の良さを邪魔しない提案ですね。

使用例

これを使えば,以下のようなコードが書けます。

func printSum(a, b string) error {
        fmt.Println(
                "result:",
                try(strconv.Atoi(a)) + try(strconv.Atoi(b)),
        )
        return nil
}

try(strconv.Atoi(a)) + try(strconv.Atoi(b)) と 1 行で書けるのが気持ちいいです。

感想

昔話になりますが,昔はエラーは os.Error という型でした。ほぼすべてのコードで os パッケージをインポートしていました。
error ビルトイン型は大きな進歩だと感じていましたが,また一歩進みますね。
実際に Go で使えるようになる前にまだ変更が入るかもしれませんが,とても楽しみな改善です。
Generics のほうの設計も進んでいるようです。
今年の GopherCon も楽しみです!

参考

Proposal: A built-in Go error check function, try
Next steps toward Go 2
error ビルトイン型

137
62
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
137
62