エラー処理の方法

エラー処理の方法として

  1. 単純な戻り値
  2. タプル
  3. 例外を投げる
  4. maybe/option/either 

といったものがある。言語によって使える方法が違うが、これらは使い分けるべきものなのか?それとも 4 または 2 にすべて置き換えるべきものなのか?

 

少なくとも 1 がよろしくない、という分かりやすい例が下記にある。

エラー処理を書いてはいけない

int foo()
{
int fd = 0, fe = 0, ff = 0;
fd = open(...);
if (fd < 0) goto finally;
fe = open(...);
if (fe < 0) goto finally;
ff = open(...);
if (ff < 0) goto finally;

...

finally:
if (fd > 0) close(fd);
if (fe > 0) close(fe);
if (ff > 0) close(ff);
return -1;
}

 この問題を タプルや option だとどう解決するか?

 

エラー処理の地学史、もしくはあなたがMaybeモナドを使うべき理由。 | 月と燃素と、ひと匙の砂糖

によれば、 hakell だとモナドを用いて

  1. func = do
  2. ok <- mayFail arg
  3. ok2 <- mayFail2 ok arg2
  4. ok3 <- mayFail3 ok2 arg3
  5. return (Just ok3)

こんな風に書けるらしいので、これを応用すればかけそう。

 ちなみに、類似の問題に対して Scala だと for-yeild を使うのがよさそう。

for式のforeach/flatMap(map)展開について - Qiita

val a = Some(1)
val b = Some("abc")
val c = Some(0.5D)

for {
  x <- a
  y <- b
  z <- c
} {
  // a, b, cすべてに値が存在する場合のみ実行される
  println(s"$x, $y, $z")
}

 Haskellの maybe, Scala の for-yield 、Rust だとどう書くのがよい?

 

あ、上記のファイルの close は RAII の文脈で考えるべきかも。 

... 調べた。

Rust の RAII は、Drop trait によって実現されているらしい。

Drop - Rust By Example

The Drop trait only has one method: drop, which is called automatically when an object goes out of scope. The main use of the Drop trait is to free the resources that the implementor instance owns.

BoxVecStringFile, and Process are some examples of types that implement the Droptrait to free resources. 

ということで、ファイルもスコープが外れると解放されるみたい。

RAII - Wikipedia

ファイルを扱うクラスのコンストラクタでファイルを開き、ファイルディスクリプタを取得する。デストラクタでファイルを閉じ、ファイルディスクリプタを解放する。こうすることにより、オブジェクトのスコープを外れた時に確実にファイルは閉じられ、ディスクリプタは解放される。資源の解放に関する問題は、これで解決。

 

個々での問題は、ファイルのオープンに失敗した場合のエラー処理方法。

上記の例だと、3つのファイルを開いたとき、全部成功した場合の処理とどれか一つでも失敗した場合の処理を、どれだけ簡潔に分離して書けるか?ということ。