try, catch, rescue

 cf. 17 トライ,キャッチとレスキュー – try, catch and rescue – Elixir

エラー

エラーの例は、例えばアトムに数を足そうとするとみることができる。

iex(1)> :atom + 1
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+(:atom, 1)

意図的にランタイムエラーを起こそうというときには、raise/1 マクロを使う。

iex(1)> raise "oops"
** (RuntimeError) oops

その他のエラーは raise/2 マクロにエラーの名前とキーワード引数を渡す。

iex(1)> raise ArgumentError, message: "Invalid argument foo"
** (ArgumentError) Invalid argument foo

モジュールの中で defexception/1 マクロを使うと、自分でエラーを定義することもできる。

iex(1)> defmodule MyError do
...(1)>   defexception message: "default message"
...(1)> end
{:module, MyError,
 <<70, 79, 82, 49, 0, 0, 14, 252, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 1, 40,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, :ok}
iex(2)> raise MyError
** (MyError) default message

iex(2)> raise MyError, message: "custom message"
** (MyError) custom message

try / rescue

エラー(例外)は、try / rescue 構造で拾うことができる。

iex(2)> try do
...(2)>   raise "oops"
...(2)> rescue
...(2)>   e in RuntimeError -> e
...(2)> end
%RuntimeError{message: "oops"}

上の例ではランタイムエラーを拾って、iex で表示するようにエラー自身を返している。

とはいえ、実際には try / rescue 構造を使うことはめったにないそうだ。というのも、例えば File.read/1 関数はファイルを開くとき、失敗したらエラーを起こすのではなく、「成功した」/「失敗した」という情報を含んだタプルを返すからだ。

iex(3)> File.read "hello"
{:error, :enoent}
iex(4)> File.write "hello", "world"
:ok
iex(5)> File.read "hello"
{:ok, "world"}

ファイルを開くときに起こりうる複数の結果(成功/失敗)に対応するには、単に case でパターンマッチングすればいい。

throw / catch

Elixir では後で値を捕れるように trhow / catch 構造が用意されている。たとえば、Enum モジュールで 13 の倍数となるような最初の値を見つけたいとしよう。

iex(6)> try do
...(6)>   Enum.each -50..50, fn(x) ->
...(6)>     if rem(x, 13) == 0, do: throw(x)
...(6)>   end
...(6)>   "Got nothing"
...(6)> catch
...(6)>   x -> "Got #{x}"
...(6)> end
"Got -39"

exit

Elixir ではすべてのコードは複数のプロセスの中で動いていてたがいにメッセージをやり取りしている。プロセスが死んだとき、exit という信号を送る。あるいは exit 信号を送ることで明示的に死ぬこともできる。

iex(7)> spawn_link fn -> exit(1) end
** (EXIT from #PID<0.80.0>) 1

Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

exittry / catch 構造でキャッチできる。

iex(1)> try do
...(1)>   exit "I am exiting"
...(1)> catch
...(1)>   :exit, _ -> "not really"
...(1)> end
"not really"

try / catch と使うことも珍しいけど、exit をキャッチするのはもっと珍しいと書いてある。
というのも、プロセスは通常、単にプロセスの exit 信号を待つだけの監視プロセスを備えた監視ツリーにくみこまれて動作していて、監視プロセスが exit 信号を受けとると、監視対象のプロセスは再起動させられるからだ。この監視システムが、エラーの後に「早く失敗する」ことでアプリケーションを既知の初期状態へ戻すことを保証している。らしい。

after

特定の動作の後、リソースをきれいに片付けられることを保証しなければならない場合、try / after が利用できる例えばファイルを開くとき、必ず閉じることを try / after で保証できる。

iex(2)> {:ok, file} = File.open "sample", [:utf8, :write]
{:ok, #PID<0.120.0>}
iex(3)> try do
...(3)>   IO.write file, "ola"
...(3)>   raise "oops, something went wrong"
...(3)> after
...(3)>   File.close(file)
...(3)> end
** (RuntimeError) oops, something went wrong

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください