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

 

 

Macbook Pro 用の外付けHDD/SSD

Macbook Air が流石に性能不足になってきたので、MacbookProに買い替えを検討する。

timemachine でバックアップを取るために、外付けHDDが必要になる。バックアップ目的のため、読み書き速度は追及しない。遅くても壊れない安定性を重視する。

容量は、1TBで十分か?

SSD

一方、Ubuntuを動かすために、外付けSSDも購入を検討する。こちらは、システムディスクとして使用するので、アクセス速度を重視する。新しいMacbookProは、thunderbolt3 な端子を持っているので、これに対応したSSDを選択する。

monomania.sblo.jp

が参考になる。

www.samsung.com

サンディスク エクストリーム ポータブルSSD

 

速度的には X5 のほうが速いらしい。

HDD

jp.transcend-info.com

WD Elementsポータブル – 大容量ストレージ | WD

My Passport - Portable Hard Drive | WD

耐久性は StoreJet が一番らしい。MILスペック。

USBメモリ

thunderbolt3 対応のUSBメモリは、容量と価格を考えるとかなり割高になる。

SSDをメインとして利用することを考えると、USBメモリは一時データを移動させるときに使うという位置づけになる。だとすると、転送速度にこだわる必要はない。したがって、通常のUSBメモリUSB3.0)が容量に対して価格がやすい。USB3.0からUSB type C への変換アダプタを使用するのが良い。

www.amazon.co.jp

 

 

 

【Rust】out引数とmap

 

rust で、関数に配列を渡したとき、アウト引数として関数内で変更できるか?

試してみた。

https://wandbox.org/permlink/ZJCko19FTfdkiZ8o

 拡張for文を使っても引数の配列に mut 指定をすることによって、変更することができた。

 一方、map を使って書こうとしたところ、配列では map は使えないみたいだった。

vector だとmap を使えるが、iter() や collect() を書く必要があるようで、今回の用途だと嬉しくない感じ。

また、map とアウト引数は同時には使えないようだ。新しくvector を生成して戻り値として返すことになる。

お買い物記録

渋谷の東急ハンズでボールペンを買った。

 

zebra.co.jp

インクが切れたときの替えは

www.zebraservice.jp

ついでに、手持ちのボールペンを調べた。

www.parkerpen.com

これっぽい。

替芯(リフィルというらしい。refill 再び満たす、かな?)は、純正品だと

スタンダード ボールペン替芯 ブラック B | Parker JP

で、Amazonだと

https://www.amazon.co.jp/dp/B0037VVXG4/

 

だが、偽物を掴ませられる可能性があるみたい。また、純正品だと輸入品になるため高い。

bunguiro.com

によると、パーカー用のアダプタ

https://www.amazon.co.jp/gp/product/B00R2J8JEQ/

と三菱の

Amazon CAPTCHA ジェットストリームのリフィルを組み合わせるほうが、ランニングコストが低いらしい。品質的にもジェットストリームは優秀なので、こちらがおすすめみたい。