Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

try!と例外

「try!は?演算子になったらしい」と助言を下さったるいも師匠が、自身のブログに?を使った例を載せている。

この例の様に、try!(および?演算子)はResultがErrであった場合の即時リターンの機構である。そう理解すれば、main()から呼んだら怒られるのは当然だ。

また、エラーが発生した場合に処理がエラーハンドラーへ送られると言う意味では、他の言語で例外を使っているケースで使用されるべきものである。

例外とは「抑制されたGOTO」であり、コードの一定範囲内からハンドラーへいつでもどこからでもジャンプさせるための制御構造のことである。余談だが、よく「例外は例外的な事象が起きたときにしか使ってはいけない」とか意味がわからない発言をするプログラマがいるが、それは名前に引きずられているだけで単なる制御構造の一種だ。単に「GOTOを乱用するのは良くないよ」と言うのと同じ意味で例外の乱用は慎むべきだし、ベストプラクティスとしてエラー処理が向いているというだけである。もちろん、そう滅多にあることではないけども「ここぞ!」という場所があったら、ためらいなくthrowすればいい。必要だから機能として存在するわけで。

話をRustに戻すと、try!は「エラー情報を持って、1呼び出し階層分脱出する例外」とみることが出来る。呼び出し元では、引数がResultであった場合にはErrの処理が義務づけられている。この機構はどことなくJavaのthrowsを想起させる。

Javaのthrowsって、例外が例外たる所以である「複数の呼び出し階層を飛び越えた大域脱出」に制限を加えている機構で、私の知る限りはあまり他の言語に類するものはない。メソッドのシグネチャをみて、どういう例外が飛んでくるのかわかるのはドキュメンテーションとしてありがたいが、全ての例外がそれでわかるわけでもないし、エラーハンドラーがあるメソッドから、例外をthrowするメソッドまでの全ての呼び出し経路にthrowsを付けていってると「これ、意味あんの?」と思わなくもない。

にもかかわらず、throwsがあった方が良いか、そうでないかの議論をあまり見ることがないのは、例外が本来持つ大域脱出の機能があまり使われることがなく、普通のプログラマレベルだとさっさとcatchしてその場にエラーハンドラを書いてしまっているか、単に握りつぶしてしまっているからなのではないかという気がする(笑)。

Rustは、JavaでいうRuntimeExceptionも含めて全ての起こりうるエラーについて「ここで処理するか、上位の呼び出しへ処理を委譲するか」を選ぶという意味でより徹底したthrowsの機構を持っていると考えればよい。戻りの型がResultになるメソッドを呼び出したら、その場でエラー処理しないのであればtry!すればよい。その場合、メソッドの戻りの型は必ずResultになるので呼び出し元が同じことを考えなければならなくなる。

ただし、メソッドの複数箇所でtry!していて、そこで発生するエラーが異なればResultの型パラメーターの扱いが難しくなる。こういう場合にどうするかと言えば、個別のエラーをまとめて扱えるようにする(型オブジェクトを使って型情報を消す)か、扱うべきエラーをまとめた新しい列挙型を作ることになる。そして、いろんなIOエラーについてあらかじめ作ってある新しい型が、るいもさんのコードに出てくるstd::io::Resultだ。「あれ?Resultって型パラメータが2つ要るんじゃないの?」と最初にコードを見たときは戸惑ったが、上のような経路を辿って考えたら当然何か仕組みがいることがわかって、納得した。Javaのthrowsの場合に複数のExceptionクラスを束ねる共通の親クラスを指定することにする場面で、このような解決をしているのだ。

このように、呼び出し階層を辿ってErrが伝搬していくので、普通の言語で例外でやろうとしていることが、ちゃんと安全にRustではtry!とResultで出来るようになっている。手軽さとは無縁のRustらしい徹底した解決法で、これはこれで納得できるやり方だ。コンパイル時に呼び出し経路がキチンと決まるので型安全を守れるし、パフォーマンスの影響も少ない。そもそもが例外は「重い処理」らしいので、Rustでは採用できないのだと思う。

そう考えると、この機能にtry!という名前がついている(いた)由来もなんとなくわかる。try!で囲っておけば、エラーの場合にErrが投げられるだからJavaのtry構文に似ているのだ。これが?になると、他の言語ではどちらかというとunwrap()に相当するもので?演算子(ぽいもの)を使っているケースが多い気がする。まあ、しょうが無いんだけど、似て非なるものがいろいろあると、混乱するな。