現状維持の議論

現状維持を支持する有効な議論があります:

既存の方法で十分

Goが初期からエラーハンドリングのための特定の構文糖を導入していれば、今日それについて議論する人はほとんどいないでしょう。しかし、私たちは15年の道のりを歩んでおり、その機会は過ぎ去り、Goは時々冗長に見えるかもしれませんが、完全に適切なエラー処理方法を持っています。

変更による新たな問題

別の角度から見ると、今日完璧な解決策に出会ったと仮定しましょう。それを言語に組み込むことは、単に一つの不満なユーザーグループ(変更を支持するグループ)から別のグループ(現状を好むグループ)に導くだけです。言語にジェネリクスを追加することを決定したときも似たような状況でしたが、重要な違いがありました:今日、誰もジェネリクスを使用することを強制されておらず、良いジェネリックライブラリは、型推論のおかげで、ユーザーがそれらがジェネリックであることをほとんど無視できるように書かれています。

対照的に、エラーハンドリングのための新しい構文的構造が言語に追加されると、事実上全員がそれを使い始める必要があります。そうしないと、彼らのコードが非慣用的になってしまいます。

言語設計の一貫性

余分な構文を追加しないことは、Goの設計ルールの一つに沿っています:同じことを行う複数の方法を提供しないこと。このルールには、「足の踏み場」が多い領域での例外があります:代入を思い浮かべてください。皮肉なことに、短い変数宣言(:=での変数の_再宣言_能力は、エラーハンドリングのために生じた問題に対処するために導入されました:再宣言なしでは、エラーチェックのシーケンスはチェックごとに異なる名前のerr変数を必要とします(または追加の別々の変数宣言)。

当時、エラーハンドリングにより多くの構文的サポートを提供する方が良い解決策だったかもしれません。そうすれば、再宣言ルールは必要なかったかもしれず、それがなくなれば、関連する様々な複雑さもなくなっていたでしょう。

良いエラーハンドリングでは冗長性が薄れる

実際のエラーハンドリングコードに戻ると、エラーが実際に_処理_されている場合、冗長性は背景に薄れます。良いエラーハンドリングには、しばしばエラーに追加情報を加える必要があります。例えば、ユーザー調査での繰り返されるコメントは、エラーに関連するスタックトレースの欠如についてです。これは、拡張されたエラーを生成して返すサポート関数で対処できます。

この(認めて人為的な)例では、ボイラープレートの相対的な量がはるかに小さくなります:

func printSum(a, b string) error {
    x, err := strconv.Atoi(a)
    if err != nil {
        return fmt.Errorf("invalid integer: %q", a)
    }
    y, err := strconv.Atoi(b)
    if err != nil {
        return fmt.Errorf("invalid integer: %q", b)
    }
    fmt.Println("result:", x + y)
    return nil
}

新しい標準ライブラリ機能

新しい標準ライブラリ機能も、Rob Pikeの2015年のブログ投稿「Errors are values」の精神で、エラーハンドリングのボイラープレートを減らすのに役立ちます。例えば、場合によってはcmp.Orを使用して一連のエラーをすべて一度に処理できます:

func printSum(a, b string) error {
    x, err1 := strconv.Atoi(a)
    y, err2 := strconv.Atoi(b)
    if err := cmp.Or(err1, err2); err != nil {
        return err
    }
    fmt.Println("result:", x+y)
    return nil
}

開発ツールの支援

コードの書き込み、読み取り、デバッグはすべて非常に異なる活動です。繰り返しエラーチェックを書くことは退屈かもしれませんが、今日のIDEは、LLM支援のコード補完さえも含む強力な機能を提供します。基本的なエラーチェックの書き込みは、これらのツールにとって簡単です。

冗長性は、コードを読むときに最も明白ですが、ツールがここでも役立つかもしれません。例えば、Go言語設定を持つIDEは、エラーハンドリングコードを隠すトグルスイッチを提供できます。このようなスイッチは、関数本体などの他のコードセクションにすでに存在します。

デバッグの利点

エラーハンドリングコードをデバッグするとき、printlnを素早く追加したり、デバッガでブレークポイントを設定するための専用の行やソース位置を持つことが役立ちます。これは、すでに専用のif文がある場合は簡単です。しかし、すべてのエラーハンドリングロジックがchecktry、または?の後ろに隠されている場合、コードを最初に通常のif文に変更する必要があるかもしれません。これはデバッグを複雑にし、微妙なバグを導入する可能性さえあります。

実用的な考慮事項

実用的な考慮事項もあります:エラーハンドリングのための新しい構文アイデアを思いつくのは安価です。そのため、コミュニティからの多数の提案が増殖しています。精査に耐える良い解決策を思いつくのは、それほど簡単ではありません。言語変更を適切に設計し、実際に実装するには、協調した努力が必要です。

本当のコストはその後に来ます:変更が必要なすべてのコード、更新が必要なドキュメント、調整が必要なツール。すべてを考慮に入れると、言語変更は非常に高価であり、Goチームは比較的小さく、対処すべき他の多くの優先事項があります。(これらの後の点は変わる可能性があります:優先事項はシフトでき、チームサイズは上下する可能性があります。)

実際のユーザーからのフィードバック

最後に、私たちの何人かは最近Google Cloud Next 2025に参加する機会がありました。そこでGoチームはブースを持ち、小さなGo Meetupも主催しました。質問できたすべてのGoユーザーは、より良いエラーハンドリングのために言語を変更すべきではないと断固として言っていました。

多くの人が、Goでのエラーハンドリングサポートの欠如は、そのサポートを持つ他の言語から新しく来たときに最も明らかであると述べました。より流暢になり、より慣用的なGoコードを書くようになると、問題ははるかに重要ではなくなります。これは、代表的であるには十分に大きな人々のセットではありませんが、GitHubで見る人々とは異なる人々のセットかもしれず、彼らのフィードバックはさらなるデータポイントとして機能します。