Goはなぜ例外(try-catch)を捨てたのか?
はじめに
Go言語を触ったとき、まず戸惑うポイントの1つが「try-catch がない」ことです。JavaやPython、C#に慣れている人にとって、これは大きな違和感になるかもしれません。
この記事では、なぜGoは例外処理構文を持たないのかという背景を設計思想から解き明かしつつ、代替手段である error の設計を紹介します。
🧠 Goの基本的なエラーハンドリング
Goでは、関数が error を返し、呼び出し側が明示的に if err != nil をチェックするスタイルが基本です。
file, err := os.Open("not_exist.txt")
if err != nil {
fmt.Println("エラー:", err)
return
}
defer file.Close()
このパターンは冗長に見えるかもしれませんが、「正常系と異常系が並列に見える」ことが、Goらしい明快さを生み出しています。
🚫 try-catch がない理由
Goが try-catch を採用しなかったのには明確な意図があります。
Go開発者たちの考え方
-
例外は制御フローの複雑化を招く
-
予測可能なエラーは“例外”ではない
-
プログラムの動作は明示的に記述すべき
たとえば file not found や JSONパース失敗 は、業務上「普通に起こり得ること」であり、それを“例外”として処理するのは、実は不自然という考え方です。
🧯 panic / recover はある
もちろん、Goにも「例外っぽい仕組み」はあります。
func dangerous() {
panic("致命的なエラー")
}
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("リカバリ:", r)
}
}()
dangerous()
}
ただしこれは、本当にプログラムが壊れる系(例:nil参照、配列外アクセスなど)でのみ使用されるべきとされています。日常的な業務エラーでは使いません。
✅ Goが選んだ「明示的なエラー制御」
Goは「明示的にエラーを扱う」ことで、以下のメリットを得ています:
| 項目 | 説明 |
|---|---|
| ✅ 読みやすい | 正常と異常の流れがフラットに見える |
| ✅ テストしやすい | モックや分岐を明示的に確認できる |
| ✅ 保守しやすい | 制御が明示的なので追いやすい |
これは、安全性と可読性を重視する設計思想に根ざしています。
🧪 じゃあ面倒なifはどうする?
たしかに if err != nil が続くのは面倒。だからこそ以下のような対応も進んでいます:\
-
errors.Is,errors.Asによるエラー種別判定 -
fmt.Errorf("%w", err)によるエラーラップ -
ライブラリ:
pkg/errors,xerrorsなど -
ユーティリティ関数で共通化(例:must(), check())
🔚 まとめ
Goは「例外を捨てた言語」ではなく、エラーを“普通の値”として扱うことを選んだ言語です。
これは一見面倒でも、コードの挙動を明示的に制御する思想の現れです。try-catch に慣れた人ほど、この発想の転換を一度体感してみてほしいです。
Discussion