@znz blog

ZnZ の memo のようなもの

Ruby 2.1.0の新機能のException#cause

| Comments

Ruby 2.1.0-preview2 リリース では 2013-11-10 の大きめの変更 が気になっていて、 ここでは Exception#cause というメソッドを紹介します。

"literal"f のように書く freeze された文字列リテラル機能が 2.1.0-preview1 には入っていたのですが、それが削除されて "literal".freeze がコンパイル時に特別扱いされるようになった、 というのも気になっています。

この投稿は Ruby Advent Calendar 2013 の3日目の記事です。

Exception#cause の例

Ruby 2.0.0 までは rescueensure の中で別の例外が発生すると、 別途保存しておかない限り、 以前に発生した例外がわからなくなってしまっていましたが、 Ruby 2.1.0(-preview2) からは別の例外を raise した時に 以前の例外が自動で保存されて Exception#cause でたどれるようになりました。 causeraise のタイミングで設定されるので、 例外オブジェクト自体は rescueensure の外で生成していても良いようです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env ruby
def foo
  raise "foo"
end

def bar
  e = Exception.new("bar")
  foo
rescue
  raise e
end

def baz
  bar
ensure
  raise "baz"
end

begin
  baz
rescue
  p $!                   #=> #<RuntimeError: baz>
  p $!.cause             #=> #<Exception: bar>
  p $!.cause.cause       #=> #<RuntimeError: foo>
  p $!.cause.cause.cause #=> nil
end

終了時のバックトレース

ちなみに、例外が保存されていても rescue せずにプログラムが終了した時のバックトレースは 今まで通り最後の例外だけ表示されるようです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
% cat t.rb
#!/usr/bin/env ruby
def foo
  raise "foo"
end

def bar
  foo
rescue
  raise "bar"
end

bar
% ruby t.rb
t.rb:9:in `rescue in bar': bar (RuntimeError)
 from t.rb:7:in `bar'
 from t.rb:12:in `<main>'

Comments