try .. rescueを使うと、ブロックの中で発生したエラーを捕捉して評価できます。
例えば、次のとおりです。
try do
raise RuntimeError, "error"
rescue
e in RuntimeError -> :error
end
tryでブロックを宣言します。raise/1を呼び出しています。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
Elixirの入門ガイドにも、次のように書かれています。
[何らかの操作を]行っているときのエラーが例外的なものかどうかは、アプリケーションが決めることです。だからこそ、Elixirは[...]関数に例外を強制しません。代わりに、どう進めるのが最善かはエンジニアが選べるようになっています。