【Ruby】raiseとは?初心者向けにわかりやすく解説

はじめに

皆さんはRubyでプログラムを書いているときに、予期しないエラーが起きた経験はありませんか。

Rubyにはエラーを扱うための仕組みがいくつか存在しますが、その中でもraiseは非常に重要な機能です。

プログラムが想定外の状態に陥った際、ただ黙って進行してしまうとバグが潜んだままになるかもしれません。

そこで、エラーハンドリングとして例外を投げる“raise”が大いに役立ちます。

本記事では、Rubyにおけるraiseの基本から実務レベルでの活用方法までを、初心者でも理解できるようにわかりやすく解説していきます。

この記事を読むとわかること

  • Rubyにおけるraiseの基本的な使い方
  • 実務における活用シーンと注意点
  • begin-rescueとの組み合わせ方法
  • カスタム例外クラスの作り方
  • エラー処理設計を考える際のポイント

raiseとは何か

プログラムを作成するとき、「想定外の状態」をどのように扱うかは重要です。

Rubyでは、予期せぬ状態やエラーが発生したときに 例外 (Exception) を投げる仕組みを持っています。

その中心的な存在が、今回のテーマであるraiseです。

例外とエラーハンドリングの概要

例外とは、プログラムが通常の処理を継続できない問題が起きたときに投げられる特別なオブジェクトです。

Rubyの場合は例外処理の機構が充実しており、エラーを引き起こす状況を明確化できるため、バグを早期に発見しやすくなります。

コードの読みやすさや保守性の向上にもつながるのが大きなメリットです。

raiseによる例外の投げ方

Rubyで例外を投げたいときには、raise "メッセージ" のように書きます。

このraiseを呼び出すと、その時点でエラーが発生したとみなし、プログラムは対応する例外を処理するための仕組みに移行します。

また、後ほど詳しく解説しますが、独自に作成した例外クラスを投げることも可能です。

基本的な使い方

ここでは、最低限覚えておきたいraiseの基本的な用法を紹介します。

メッセージ付きの例外

何らかの異常が発生したことを開発者や利用者に伝えるため、メッセージを添えるのが一般的です。

def divide(a, b)
  if b == 0
    raise "0で割ることはできません"
  end
  a / b
end

puts divide(10, 2) #=> 5
puts divide(10, 0) #=> RuntimeError: 0で割ることはできません

上記の例では、分母が0の場合にraiseを使って例外を発生させています。

これにより、「分母が0だとエラーになる」という仕様を明確に示すことができます。

どんなときに使うのか

  • 計算において予測不可能な値が来たとき
  • 引数に不正な値が渡されたとき
  • ファイルの読み書きができなかったとき

エラーメッセージを表示するだけでなく、プログラムの流れを停止して“ここで何かおかしいぞ”と知らせるために利用します。

そのため、例外の内容を正確に伝わるように定義することが大切です。

使いどころと実務でのイメージ

実際の開発現場では、どのようなシチュエーションでraiseが使われるのでしょうか。

Webアプリケーションにおける例

例えば、Webアプリケーションの中で、ユーザーがフォームに入力した値をバリデーションする場面を想像してください。

名前やメールアドレスといった必須フィールドが空欄のときは、エラーとして処理を止めたい場合があります。

このとき、Ruby on Railsなどのフレームワークではバリデーション機能が充実していますが、独自のロジックを追加するときにはメソッド内でraiseを使って異常を知らせる実装をすることがあります。

バッチ処理やスクリプトの中で

大量のデータを一括で処理するバッチスクリプトや、ファイル操作を行うスクリプトでも、raiseを使うシーンは多々あります。

特にファイルが見つからないときや、ネットワーク通信に失敗したときなど、処理を中断すべき状況で積極的に例外を投げることで、誤ったデータの混入や無限ループを防止するのです。

大規模プロジェクトでのメリット

大人数で開発する場合、コードの意図をしっかり伝えることは非常に重要です。

raiseを使ってエラーが起きるタイミングを明示することで、“このメソッドでは〇〇の場合には例外が発生する”という情報を他の開発者にわかりやすく示せます。

一方でraiseを使いすぎるとコードが雑多になってしまうこともあるので、適切な箇所に厳選して仕込むのがポイントと言えるでしょう。

例外オブジェクトの指定

raiseにはエラーメッセージだけでなく、例外クラスを指定することもできます。

例外クラスを指定する書き方

以下のように、クラス名とメッセージを引数に取ります。

raise ArgumentError, "引数が不正です"

ArgumentErrorはRubyの組み込み例外クラスの一つです。
このように既存の例外クラスを使うことで、具体的にどんな種類のエラーかを示すことができます。

組み込み例外クラスの例

Rubyには多彩な組み込み例外クラスがあります。
代表的なクラスをいくつか挙げてみます。

RuntimeError

特にクラスが指定されていない例外が発生したときに使われるクラス

ArgumentError

メソッドに渡される引数が正しくない場合に使われるクラス

NoMethodError

呼び出したメソッドが定義されていない場合に使われるクラス

StandardError

Rubyにおけるほとんどの例外のスーパークラス

このように、目的に応じて既存クラスを使い分けることで、エラー内容をさらに明確化できます。

メッセージとバックトレースの活用

例外には、メッセージとあわせて バックトレース (エラーが起きた箇所までの呼び出し履歴) が含まれます。

これを活用すると不具合の原因を特定しやすくなります。

メッセージの重要性

現場では、エラーの内容が明確に伝わるかどうかが非常に大切です。

曖昧なメッセージだけが表示されると、原因を特定するのに無駄な時間がかかってしまいます。

そのため、raise "何が原因でエラーが発生したかを簡潔に伝えるメッセージ" のように、エラー箇所だけでなく状況や操作などを示す言葉を含めるよう意識するとよいでしょう。

バックトレースを活用したデバッグ

バックトレースは「どのメソッドがどこから呼び出されたか」を一覧で示してくれる仕組みです。

エラーが発生したときにバックトレースを確認すれば、アプリケーション内でどのような処理を経由してエラーに至ったのかが一目瞭然です。

実務ではバグ対応のスピードを上げる大きな手助けになるので、ログに出力するなどして積極的に活用すると便利です。

begin-rescue-end との組み合わせ

raiseを使って例外を投げたら、当然どこかでそれを受け止めて対処する必要があります。

Rubyで例外を受け止めるには、begin-rescue-end構文を用います。

基本形

下記のコードは、例外処理の流れをシンプルに示したものです。

begin
  # ここで何らかの処理を行う
  raise "エラーが発生しました"
rescue => e
  puts "捕まえたエラー: #{e.message}"
end

beginブロック内でraiseを呼び出すと、すぐにrescue節へ制御が移ります。

そして、rescue => eeには例外オブジェクトが格納されるため、e.messagee.classを参照してエラーの情報を調べることができます。

実務で多用されるパターン

実務では以下のようなパターンがよく使われます。

特定の例外クラスを捕捉する

複数のrescueを並べて、例えばArgumentErrorだけを捕まえるなどが可能

ログ出力した後、再度raiseする

ログを残しつつ、さらに外側で処理してもらうために例外を投げ直す

最終的なエラー処理を一本化する

上位のレイヤーでまとめて例外をキャッチし、ユーザーに知らせたり再試行させたりする

このようにbegin-rescue-endraiseをセットで活用することで、システム障害の早期発見や被害の最小化が期待できます。

ensureを使った後処理

例外が発生してもしなくても、必ず実行したい処理がある場合に使うのがensureです。

例えば、ファイルを開いたら例外が起きてもクローズ処理は絶対にしたいときがありますよね。

def read_file(path)
  file = File.open(path, "r")
  content = file.read
  content
rescue => e
  puts "エラー発生: #{e.message}"
ensure
  file.close if file
  puts "ファイルをクローズしました"
end

上記の例では、ファイルを開いた後、何らかのエラーが起きても最後にfile.closeが実行されます。

これは実務でもよく使われるパターンで、リソースリークを防ぐために欠かせないテクニックです。

retryを使ったリトライ処理

raiseと組み合わせて覚えておきたいキーワードにretryがあります。

エラーが起きたら再度同じ処理を試みたいケースがある場合に便利です。

retryのイメージ

例えば、WebAPIをコールしてデータを取得するとき、ネットワーク状況によりまれに失敗することがあります。

そのとき、1回の失敗で諦めずにリトライする実装が求められるかもしれません。

begin
  # APIコール
  response = call_api()
  raise "APIエラー" unless response.success?
  puts "取得したデータ: #{response.body}"
rescue => e
  @retries ||= 0
  @retries += 1
  if @retries < 3
    puts "リトライします (#{@retries}回目)"
    sleep(1)
    retry
  else
    puts "エラー: #{e.message}"
  end
end

ここでは、初回のrescueブロックに入ったときにretryを呼ぶことで、再びbeginの先頭から処理を繰り返します。

回数制限を設けないと永遠にリトライし続ける可能性があるため、実務では上記のようにカウンターを設置して一定回数までに留めるのが一般的です。

カスタム例外クラス

Rubyでは、組み込みの例外クラスを使うだけでなく、自分で例外クラスを定義することもできます。

カスタム例外クラスの作り方

自作の例外クラスを使うと、エラーの性質をより明確に分けて扱えます。

class MyCustomError < StandardError
end

def do_something_risky
  # 危険な処理
  raise MyCustomError, "特別なエラーが発生しました"
end

begin
  do_something_risky
rescue MyCustomError => e
  puts "カスタム例外を捕まえました: #{e.message}"
end

このようにStandardErrorを継承する形で独自の例外クラスを用意し、必要に応じてエラーの詳細を保持するインスタンス変数なども追加できます。

実務での利用例

ファイル操作専用のエラー

自作クラスFileOperationErrorを定義して、ファイル入出力に関するエラーを一括でハンドリング

APIエラーの細分化

ApiResponseErrorAuthorizationErrorなど用途に合わせたクラスを作ることで、エラー原因を素早く切り分けられる

このように、カスタム例外クラスを活用すればエラーの管理がより明確になります。

例外処理の設計における注意点

例外処理はプログラム全体の安定稼働に関わるため、考えなしにraiseを乱発すると保守性を下げる恐れがあります。

ビジネスロジックと例外の関係

実務では“ビジネスロジック”と“エラー状態”をしっかり区別することが肝心です。

例えば、あくまでもビジネスルール上の不正(「ユーザーがすでに登録済み」など)はエラーではなく、バリデーションロジックとして扱った方が合理的な場合があります。

一方、本来起こり得ない状態(ファイルが絶対に存在するはずなのに見当たらないなど)に陥ったら、それはraiseで想定外エラーとして扱うのが適切です。

過剰に例外を使わない

何でもかんでも例外にしてしまうと、呼び出し側は常にrescueを意識しなければいけません。

結果としてコードが読みにくくなり、トラブルシュートも複雑化してしまいます。

「あらかじめチェックできるものは、可能な限り例外を使わずに防ぐ」
この方針を念頭に置くと、raiseとバリデーションや条件分岐の使い分けがうまくいきやすいでしょう。

ログの活用

実務では例外が起きたタイミングで必ずログを残すことが多いです。

ここでいうログは、サーバーやアプリケーションに蓄積される情報です。

例えば、どんな入力値でエラーが起きたのか、どの端末からアクセスしたのか、といった付随情報をまとめて残すことで、開発者が調査するときの手がかりとなります。

raiseと他のエラーハンドリングメソッドとの違い

Rubyではraiseの別名としてfailというメソッドも存在します。

どちらも例外を発生させる点は同じですが、Rubyのコーディング規約などでは以下のような使い分けがされることが多いです。

fail

メソッド内部で使用し、“ここを通っちゃいけないんだけど念のため”のような文脈で用いる

raise

通常のエラーハンドリングや、あえて意図的に例外を投げたいときに用いる

ただし、実際には機能面での差異はほぼありません。
プロジェクト内のコーディングスタイルに従うとよいでしょう。

実務でのトラブルシューティング例

ここでは、実際の業務を想定した具体的なトラブルシュートの流れを見てみます。

シナリオ

ある日、決済システムのバッチ処理が“予期せぬエラー”で失敗したという連絡を受けました。

コードを見ると、以下のようにraiseが仕込まれています。

def process_payment(user_id, amount)
  user = find_user(user_id)
  if user.nil?
    raise "ユーザー情報が見つかりません: user_id=#{user_id}"
  end

  # ...支払い処理
  if amount < 0
    raise ArgumentError, "支払金額が不正です: #{amount}"
  end

  # 決済API呼び出し
  # ...
end

バッチ処理では多数のユーザーに対してprocess_paymentを呼んでいます。

調査ポイント

1. どの行でエラーが起きたのか

バックトレースから問題発生箇所を特定

2. どのユーザーIDでエラーが出ているのか

エラーメッセージにuser_id=#...のように入れることで原因特定が速くなる

3. ログ出力やretryの仕組みはあるか

単にエラーが出たときに処理をやめるだけでなく、リトライが必要かどうかを検討

このように、Rubyでエラーが起きてもraiseによってエラーメッセージとバックトレースが明確に示されるため、比較的スムーズに原因を追跡できます。

状況によっては決済システムでのリトライが危険な場合もあります。
どんなエラーがどのタイミングで起きるかを把握し、何をトリガーにリトライするかを事前に検討しておきましょう。

テストコードにおける例外の扱い

実務では、テストコードを書くことが推奨されます。

その際、例外が正しく発生するかどうかを確認するケースも多いです。

RSpecでの例

たとえばRSpecを使う場合、以下のように書いてraiseの動作を確認します。

RSpec.describe "divide" do
  it "0で割ろうとするとエラーになること" do
    expect { divide(10, 0) }.to raise_error(RuntimeError, "0で割ることはできません")
  end
end

上記のコードでは、divide(10, 0)を実行した結果としてRuntimeErrorが発生し、そのメッセージが"0で割ることはできません"であることをテストします。

これにより、想定通りのエラーが発生することを自動的に検証できます。

まとめ

ここまで、Rubyのraiseについて初心者の方にも理解しやすいように紹介してきました。

raiseはプログラムを中断してエラーを明確に伝える重要な機能です。

エラーが起きたときに必ず誰かが気づき、原因を追跡できる仕組みを整えることは、実務では欠かせません。

  • 想定外の状態が起きたらraiseで例外を投げる
  • begin-rescue-endで例外をキャッチしてエラー処理を行う
  • 適切な例外クラスやメッセージを使って原因追跡しやすくする
  • 過剰なraiseは避け、バリデーションや条件分岐とのバランスをとる

これらのポイントを押さえれば、Rubyでの開発がよりスムーズになることでしょう。

過度に複雑なエラー設計をすると、逆にデバッグが難しくなる場合があります。
まずはシンプルに始めてみて、必要に応じて調整するのがコツです。

もし何か予期せぬエラーに遭遇しても、raiseを上手に使えば「どこで問題が起きているのか」を早めに把握できます。

皆さんもぜひ実際の開発や学習を通して、raiseを使いこなしてください。

Rubyをマスターしよう

この記事で学んだRubyの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。