エラー処理のサンプルコード

Wandboxで、Rust と Scala で Result/Either を使ったサンプルコードを書いてみた。

 

[Rust Result sample ](https://wandbox.org/permlink/nbwQkC94TZwY4lix)

[Scala Either sample](https://wandbox.org/permlink/ExJHa7TPKRplXSym)

 

 エラーと言っても種類がある。

 

アプリケーションの責任範囲外で正常に終了できない場合

例外処理でよい。エラー情報を記録して、アプリケーションを終了する。

 

例:

  • メモリ確保失敗(メモリ不足、メモリのCRCエラーなど)
  • ファイル書き込みエラー(ディスクフル、ディスク書き込み不良など)
  • 通信エラー(断線等のハードウェアの故障)

正常状態に復帰可能

  • ファイルオープンエラー(ファイル名間違い、パス名間違い)
  • ファイル書き込みエラー(アクセス権の設定ミス)
  • 通信エラー(電波状態不良、通信帯域が混み合っている)

失敗した処理を再度行い、正常終了するか試す。

maybe/option, Result/Either などを使う。

 

 

電子マネーの使い分け

自分の場合、どういう使い分けがお得になるのか調べた結果をここにまとめておく。

前提条件

  • 現在使っているメインのクレジットカードは楽天カード
  • クレジットカードは増やしたくない
  • リスク分散のため、スマホを使う決済方法とカードだけの決済方法の2種類を用意したい

使い分け

高額(2万円以上)の支払い

クレジットカード。

 

少額決済(2万円未満)

カードのみで決済

加盟店によって使えたり使えなかったりする。これらの電子マネーは事前にチャージしておくタイプ。ポイントの観点では、Edyが一番良い。楽天カードからEdyにチャージする際にもポイントがつくため。使用できる店舗数ではSuica/Pasmoが多い。

スマホ決済

ポイントの観点では、楽天カード利用者は楽天Payが有利。加盟店の多さでは QuicPayが有利。

オートチャージについて調べてみた

普段のコンビニでの買い物などには、Pasmoを使っている。残高が減ったら現金を駅の券売機でチャージしている。カードをなくした時のリスクを考えてプリペイド型の電子マネーを使ってきた。が。そろそろオートチャージを試してみようかと調べてみた。

 

PASMO

オートチャージにすることも可能だが、その場合は東急のクレジットカードを新たに作る必要がある。クレジットカードは増やしたくない。

 

iD/QuicPay

これらはクレジットカード決済(後払い)なので、そもそもオートチャージの必要がない。

デメリットは、カードをなくした時のリスクだが、口座を普段使い用のものにして、残高を制限すればリスクを低減できるはず。

 

現在メインで使っているカードを Apple Pay と組み合わせて使えそう。

Edy

これもオートチャージできる。クレジットカードでオートチャージできるし、ApplePay  でもオートチャージできることが判明。

【Rust】Option/Result の使い方

Scala の for- yield 文のように、 Rust で複数の Option/Result をすっきり書くにはどうしたらよいか?

まず、Scala の場合。

https://wandbox.org/permlink/tRZ454NozWaAMzd2

 

これと同じことを Rust で書くには?

Rust のエラー処理

昨日の続き。良い記事を見つけた。

rustのエラー処理と疑問符演算子

複数のResult<>値を扱うには、疑問符演算子 ? を使うのがいいらしい。

Scala のfor yield に対応するもののはずなので、いくつかサンプルコードを書きたい。試してみるが書式を理解できていないようで、まだコンパイルが通らない(汗

また、いまどきのrust では、main関数で疑問符演算子を使えるらしい。

? in main and tests - The Edition Guide

Optionも使えるようだ。

Rust のエラーハンドリングはシンタックスシュガーが豊富で完全に初見殺しなので自信を持って使えるように整理してみたら完全に理解した - Qiita

 

エラー処理の方法

エラー処理の方法として

  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つのファイルを開いたとき、全部成功した場合の処理とどれか一つでも失敗した場合の処理を、どれだけ簡潔に分離して書けるか?ということ。