Falling in love with Rust

Rust のいいところ

エラーハンドリングが美しい

エラーハンドリング問題は、二つの異なるエラー、つまり

の二つがしばしば混同されてしまうことで難しくなってしまっている。

error handling suffers from an infamous mechanical problem: for a function that may return a value but may also fail, how is the caller to delineate the two conditions?

「エラーを返しうる関数」を呼び出す時に、この二つをどうやって区別すべきか?というのがエラーハンドリングを苦しめてきた悪名高い構造的問題である。

C の時代、そこはプログラマー任せだった。システム、プログラムによってどうやってそれを取り扱うかが違う。

C++ / Java の時代、「例外」という考え方が一般的になった。しかし、「例外」は関数呼び出し側に毎回負担をかけてしまうことで、実行速度と開発速度両方に負担をかけるという点で良くない。(し、オペレーションエラーがプログラムロジックのエラーになりうる)

JavaScript や Go ではまた違ったアプローチもしている。JavaScript ではエラーを引数として渡してしまう?Go は、複数の戻り値を利用することで常に「成功」と「失敗」の両方を返すようになっている。Go のアプローチは面白いが、呼び出し側の処理が結構面倒。

Rust では更に新しいアプローチを取っている。一級幾何データ型を利用する?

これは、列挙された型の一つであり、プログラマがその内のどれか一つを明示する必要があるような型

これを利用して、ある関数が「二つの型のどちらか一方」であるような値を返すことが可能になっている。つまり、呼び出し側は**「成功」か「失敗」かどちらかを表す一つの返り値を受け取る**。これをパターンマッチ式に渡すことで、成功時の処理と失敗時の処理を自然に書き分けることができる。(何もしないこともできる!)

By contrast, Rust takes an approach that is unique among systems-oriented languages: leveraging first algebraic data types — whereby a thing can be exactly one of an enumerated list of types and the programmer is required to be explicit about its type to manipulate it — and then combining it with its support for parameterized types. Together, this allows functions to return one thing that’s one of two types: one type that denotes success and one that denotes failure.

コードにすると、こんな感じだ。

fn do_it(filename: &str) -> Result {
		// fs::metadata(filename) の返り値を match 式に渡す
    let stat = match fs::metadata(filename) {
        Ok(result) => { result },
        Err(err) => { return Err(err); }
    };                  

		// File::open(filename) の返り値を match 式に渡す
    let file = match File::open(filename) {
        Ok(result) => { result },
        Err(err) => { return Err(err); }
    };

		// 以下同様
    /* ... */

    Ok(())
}

さらに、これを自動 unwrap することもできる。下のコードは上と同じことをする。めっちゃ書きやすいぜ!!!!

fn do_it_better(filename: &str) -> Result {
    let stat = fs::metadata(filename)?;
    let file = File::open(filename)?;

    /* ... */

    Ok(())
}