著作一覧 |
class TestError < RuntimeError def to_str "foobar" end end begin raise TestError.new('hello') rescue TestError => e puts 'OK' rescue => e puts e.class #=> RuntimeError puts e.message #=> foobar end
なぜかRuntimeErrorに変わってしまう。もちろんクラス指定でrescueはできない。
これは仕様なのかバグなのかよくわからないが、理由は、raiseの第1引数がStringだと判断されてしまうからだ。というのは、TestErrorクラスにto_strが実装されているからだ。
これが仕様(raiseに第1引数はto_str実装クラスを含むStringの一種ならばそれをmessageに持つRuntimeErrorをスローする)なのか、バグあるいは親切にしすぎ(間違えてHashやArrayを渡された場合にto_strすることで救済する)なのかはどちらでも良い気がするが、つまるところ、例外クラスを独自に作成する場合は、to_strメソッドを実装してはだめということだ。定義した瞬間にそのクラスは意味がなくなる(上の例なら黙ってraise 'foobar'を実行すれば良いことになる)からだ。
ジェズイットを見習え |
to_sならともかく、to_strは暗黙の変換なのですから定義したら異常なことになるのは当然でしょう
そう来るとは思わなかったけど(to_strがやばいメソッドだというのはその通りだが)、<br>@@ -768,6 +768,8 @@<br> exc = argv[0];<br> if (NIL_P(exc))<br> break;<br>+ if (rb_obj_is_kind_of(exc, rb_eException))<br>+ goto exception_call;<br> if (isstr) {<br> mesg = rb_check_string_type(exc);<br> if (!NIL_P(mesg)) {<br>のほうが良くない?
と、パッチを作ったわけだが、そもそもわざとException派生かどうかよりもto_strを優先しているかを知りたかったのだけど、成瀬さんの「当然」というのはto_str優先に思えるけど、「異常」だというのも認識のうちなら異常じゃないようにすれば良いんだからパッチを当てるべきな気もするなぁ。
異常なことをする人のためにわざわざ処理を追加する必要はあんまり感じないかなぁ。<br>というスタンスの人がおそらく多いので、そもそもException派生かto_str優先かは処理系およびバージョン依存という感覚で、変えて速くなるなら変えるけど、わざわざいじって遅くするモチベーションはない感じ
まあ、そうですね(というか、そこに同意できなければredmineに書いてるわけだし)。