Go言語のエラー処理を「Must」関数で簡略化しよう

JavaScriptを有効にしてください

まえがき

Go言語で開発を進めると、例えばファイルを開く際やテンプレートを解析する際など、多くの関数がエラーを返すことに気づくでしょう。そのたびにエラー処理のコードを書くのは、場合によっては面倒に感じるかもしれません。

Goの標準ライブラリの一部のパッケージでは、エラー処理を簡略化するためのヘルパー関数が用意されています。このパターンの代表例が「Must」です。ただし、これは全ての関数に共通する仕組みではなく、特定の用途(主に初期化処理など)に限定されています。

Must関数とは?

「Must」パターンは、対象の関数がエラーを返した場合に、エラーをそのまま無視せず、即座にプログラムを停止(panicするなど)させるためのヘルパー関数です。
Goの標準ライブラリでは、たとえば regexp.MustCompiletemplate.Must のように、エラーが発生してはならない初期化処理で用いられるケースが見受けられます。

主に以下のような場面で利用されます:

  • 初期化処理など、エラーが発生することがほぼ考えられないケース
  • エラーが発生した場合に、早急に修正すべき設定や開発時の構成

なお、「Must」パターンはエラー発生時にプログラムを即座に停止させるため、実運用環境やリソースのクリーンアップが必要な場面での使用は十分に検討する必要があります。

使用例

たとえばテンプレートを作成する場合、通常の方法だとエラーチェックが必要です。

1
2
3
4
tmpl, err := template.New("example").Parse("Hello, {{.}}!")
if err != nil {
    log.Fatal(err)
}

しかし、「Must」を使えば、次のようにシンプルになります。

1
2
// エラーが発生するとパニックが起きて即座に止まる
tmpl := template.Must(template.New("example").Parse("Hello, {{.}}!"))

コードが一気にスッキリしましたね。

regexp.MustCompile の例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
    "fmt"
    "regexp"
)

var re = regexp.MustCompile(`\d+`)

func main() {
    fmt.Println(re.FindString("Hello 123 World!")) // "123"
}

通常 regexp.Compile()(regexp.Regexp, error) を返しますが、
MustCompile を使うことで エラーハンドリングを省略し、コンパイル時に確実にエラーを検出 できます。

今の所Must関数が実装されているのは公式では上記2つのパッケージだけのようです。
ですが、他のサードパーティー製のライブラリでも広く使われている手法なのでぜひ活用してください。

自分でMust関数を作る方法

また、自分でMust関数を定義することも可能です。

1
2
3
4
5
6
func must[T any](v T, err error) T {
    if err != nil {
        log.Fatal(err)
    }
    return v
}

この関数を使うことで、さまざまなエラー処理を簡潔に書くことができます。

1
2
file := must(os.Open("example.txt"))
defer file.Close()

※この後の解説でも書いてますが、上記コードは絶対にファイルが存在しているようなパターンを前提としています
失敗する可能性がある場面ではmust関数ではなく適切なエラー処理を書く必要があります

Must関数を「使ってはいけない」パターン

Goの標準ライブラリや自作のMust関数は便利ですが、使う場面を間違えると問題を引き起こします。
ここでは、Must関数を使ってはいけない代表的な例をご紹介します。

ユーザー入力や外部データを扱う場合

ユーザーの入力や外部APIの結果など、エラーがいつ発生するかわからない場面では、Must関数は使わないようにしましょう。

1
2
// 悪い例
userID := must(strconv.Atoi(userInput))

このコードは、ユーザーが間違った入力をした瞬間に即座にpanicし、アプリがクラッシュします。

なぜこれらはMust関数を使ってはいけないのか?

Must関数はエラーが起こらない前提で利用するものであり、万が一エラーが起きた場合に即座に停止(panic)するためです。これを不確定な要素が多い場面で使うと、ユーザー体験を大きく損なったり、予期せぬクラッシュが発生します。

正しいエラー処理

不確実性がある場面では、丁寧にエラー処理を行い、エラーが発生しても安全に処理できるようにしましょう。

1
2
3
4
5
6
// 良い例
userID, err := strconv.Atoi(userInput)
if err != nil {
    // ユーザーに適切なエラーメッセージを表示する
    log.Println("不正な入力です")
}

Must関数は便利ですが、「絶対にエラーが起きない前提」の場合のみ使用しましょう。不確実なデータや外部リソースには丁寧なエラー処理を心がけてください。

あとがき

Go標準ライブラリにも採用されているMust関数は、エラー処理を簡単かつ明確にしてくれる優れものです。ただし、「絶対に失敗しないことが前提の処理」にのみ利用するようにしましょう。

ぜひ活用して、読みやすいGoコードを書いていきましょう。


スポンサーリンク

共有

もふもふ

プロフィール

著者
もふもふ
プログラマ。汎用系→ゲームエンジニア→Webエンジニア→QAエンジニア