tr

try/rescue の Elixir

1個の演習

try/rescueについて

try .. rescueを使うと、ブロックの中で発生したエラーを捕捉して評価できます。

例えば、次のとおりです。

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end
  • 1行目では、tryでブロックを宣言します。
  • 2行目には、エラーを発生させる可能性のある関数呼び出しを書きます。ここではraise/1を呼び出しています。
  • 4行目では、rescueセクションで、発生したエラーの_モジュール_名に対してパターンマッチを行います。
    • ->の左側:
      • eはエラー構造体にマッチします。
      • inはキーワードです。
      • RuntimeErrorはマッチ対象のモジュールですが、任意のエラーモジュールにマッチさせることも、_ですべてのエラーにマッチさせることもできます。
    • 右側:
      • エラーがマッチしたときに実行される命令です。

エラー構造体

この方法でrescueしたエラー(「例外」とも呼ばれます)は、構造体です。エラー構造体が異なれば、キーも異なります。標準ライブラリの「例外」セクションには、あらかじめ定義されているすべてのエラーの一覧があります。

# ArithmeticError caused by division by zero
%ArithmeticError{message: "bad argument in arithmetic expression"}

# Protocol.UndefinedError caused by passing `nil` to `Enum.count/1`
%Protocol.UndefinedError{description: "", protocol: Enumerable, value: nil}

Elixirでエラーをrescueすることは、めったにありません。ふつうは、rescueしたエラーをログに記録するか、外部の監視サービスに送信してから、もう一度raiseします。つまり、特定のエラー構造体の内部構造を気にすることは、ほとんどないということです。

例外の概念では、独自のエラー構造体を定義する方法を説明しています。

アンチパターンを避けましょう

  • エラーを使って論理の流れを制御するプログラミングパターンは避けましょう。これはElixirではアンチパターンです。

    # Avoid using errors for control-flow.
    try do
      {:ok, value} = MyModule.janky_function()
      "All good! #{value}."
    rescue
      e in RuntimeError ->
        reason = e.message
        "Uh oh! #{reason}."
    end
    
    # Rather, use control-flow structures for control-flow.
    case MyModule.janky_function() do
      {:ok, value} -> "All good! #{value}."
      {:error, reason} -> "Uh oh! #{reason}."
    end
    

raiseすべきか、raiseすべきでないか、それが問題です

Elixirの入門ガイドにも、次のように書かれています。

[何らかの操作を]行っているときのエラーが例外的なものかどうかは、アプリケーションが決めることです。だからこそ、Elixirは[...]関数に例外を強制しません。代わりに、どう進めるのが最善かはエンジニアが選べるようになっています。

GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

try/rescueを学習する