if err != nil — Rubyのbegin/rescueが恋しくなる理由
Goは例外を使わない。エラーを値として返す。コードの半分がエラー処理になる。
Ruby開発者がGoを初めて使う時、最もショックを受ける部分だ。
Rubyではエラーが出ると例外(Exception)が投げられ、どこかでrescueが捕まえる。ハッピーパスだけ書いて、エラーはrescueブロックでまとめて処理。
Goは完全に違う。例外がない。エラーは関数の返り値だ。
Ruby vs Go エラー処理比較
Ruby:
begin
data = File.read("config.yml")
config = YAML.parse(data)
save(config)
rescue => e
puts "エラー: #{e.message}"
end
Go:
data, err := os.ReadFile("config.yml")
if err != nil { return err }
config, err := yaml.Unmarshal(data)
if err != nil { return err }
err = save(config)
if err != nil { return err }
見えるか?Ruby: ハッピーパス3行 + rescue 1行。Go: 実際のロジック3行 + エラーチェック6行。if err != nilが関数呼び出しのたびに繰り返される。
なぜこの設計なのか
Goチームの哲学:エラーは無視してはならない。例外ベースのシステムではrescueを書き忘れるとエラーが静かに伝播する。Goではエラーを受け取り明示的に処理しなければならない。しなければコンパイラが「err declared but not used」とエラーを出す。
errors.Is、errors.As — Rubyのis_a?のようなもの
Rubyでrescue ActiveRecord::RecordNotFoundのように特定の例外クラスを捕まえるように、Goではerrors.Is(err, sql.ErrNoRows)やerrors.As(err, &target)でエラー型を確認する。
panic/recover — 本当に使うな
Goにもpanic()がある。Rubyのraiseに似ている。しかしGoコミュニティではpanicは「プログラムが続行できない」深刻な状況でのみ使う。HTTPリクエスト処理中のエラーはpanicではなくerror返却。
Rubyでraise/rescueを日常的に使うのとは文化が全く違う。
RubyからGoへ
Ruby: begin/rescue/end → Go: if err != nil { return err }(毎回呼び出しごと)
Ruby: raise CustomError → Go: errors.New("message")またはfmt.Errorf()
Ruby: rescue SpecificError → Go: errors.Is(err, target) / errors.As(err, &target)
Ruby: raise(日常的)→ Go: panic(プログラム終了レベルでのみ、日常的使用禁止)
メリット
- ✓ エラーを無視できない — コンパイラが強制。Rubyの「rescue書き忘れて本番で爆発」問題を防止
- ✓ エラーフローが明示的 — コードを読めばどこでエラーが発生しどう処理されるか見える
デメリット
- ✗ if err != nilがコードの40〜50%を占有 — 視覚的ノイズが酷い
- ✗ Rubyのretryパターン(rescue → retry)がなくリトライロジックを自前で実装する必要がある