🚨

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へ

1

Ruby: begin/rescue/end → Go: if err != nil { return err }(毎回呼び出しごと)

2

Ruby: raise CustomError → Go: errors.New("message")またはfmt.Errorf()

3

Ruby: rescue SpecificError → Go: errors.Is(err, target) / errors.As(err, &target)

4

Ruby: raise(日常的)→ Go: panic(プログラム終了レベルでのみ、日常的使用禁止)

メリット

  • エラーを無視できない — コンパイラが強制。Rubyの「rescue書き忘れて本番で爆発」問題を防止
  • エラーフローが明示的 — コードを読めばどこでエラーが発生しどう処理されるか見える

デメリット

  • if err != nilがコードの40〜50%を占有 — 視覚的ノイズが酷い
  • Rubyのretryパターン(rescue → retry)がなくリトライロジックを自前で実装する必要がある

ユースケース

Railsコントローラのrescue_fromパターンをGo HTTPハンドラのエラー返却に転換する時