Rustでエラーを合成する

合成したエラー型を使う
---------------------
Rustには例外がない。
全ての失敗するかもしれない計算はOptionやResultを使って処理する。

```rust
use std::{fs, io};

fn read_file(filename: &str) -> Result<String, io::Error> {
    let mut f = fs::File::open(filename)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}
```

`?`演算子が実装されたことで記述も簡単になった。
この例では二つの`?`が使用されているが、これらはともに`io::Error`を返すので`Result<String, io::Error>`を使用できる。
しかし、別のエラーが発生する場合はどうだろう？例えばデータがmsgpackで保存されていたとすると:

```rust
use serde::Deserialize;
use rmp_serde::{decode,Deserializer};

fn read_msg<'de, T: Deserialize<'de>>(filename: &str) -> Result<T, ???> {
    let buf = fs::File::open(filename)?;          // may throw io::Error
    let mut de = Deserializer::new(&buf);
    let obj = Deserialize::deserialize(&mut de)?; // may throw decode::Error
    Ok(obj)
}
```

もちろんこのコードは動かない。`Result`がちゃんと定義されていないからだ。
ではここにはどんなエラーを入れればいいのだろうか？以前は`Box<std::error::Error>`を使用していたが、準標準（？）ライブラリの[rust-lang-nursery/failure](https://github.com/rust-lang-nursery/failure)では`failure::Error`という型が用意してある[^nursery]：

[^nursery]: nurseryは「養成所」の意味。Rust本体にあった方が良い機能をrust本体とは別に作っている。

```rust
extern crate failure;

fn read_msg<'de, T: Deserialize<'de>>(filename: &str) -> Result<T, failure::Error> {
    let buf = File::open(filename)?;
    let mut de = Deserializer::new(&buf);
    let obj = Deserialize::deserialize(&mut de)?;
    Ok(obj)
}
```

この`failure::Error`は標準ライブラリのエラー型の他、次に説明する`failure::Fail` traitを実装したエラーを取れるように出来ているため、簡単にエラーを合成して使う事ができる。

エラー型を自分で定義する
--------------------------

単純なプロジェクトでは`failure::Error`で十分だろう。しかしライブラリとして提供する場合、
また複数人数で大規模なプロジェクトではちゃんとしたエラー型を定義しておく方が都合が良い。
failureではこのために`Fail` traitが用意してある：

```rust
#[macro_use]
extern crate failure;

#[derive(Debug, Fail)]
enum LoadError {
    #[fail(display = "Msgpack decode failed: {:?}", error)]
    DecodeError {
        error: decode::Error,
    },

    #[fail(display = "Some I/O Error: {:?}", error)]
    IOError {
        error: io::Error,
    }
}
```

このようにfailureでは`Fail` traitをproc_macroによって自動実装している。`#[fail(display = "...")]`の行ではこのエラーにどのように`Display`を実装するのかが記述されている。

`DecodeError`や`IOError`には元のエラーの他に追加で情報を記録することもできるが、今回は単純にして、さらに`From`で変換することにしよう：

```rust
impl From<decode::Error> for LoadError {
    fn from(error: decode::Error) -> Self {
        LoadError::DecodeError { error }
    }
}

impl From<io::Error> for LoadError {
    fn from(error: io::Error) -> Self {
        LoadError::IOError { error }
    }
}
```

`?`演算子ではエラー型には自動的に`Into`が適用されるので以下のように使える：

```rust
fn read_msg<'de, T: Deserialize<'de>>(filename: &str) -> Result<T, LoadError> {
    let buf = File::open(filename)?;               // 自動でio::Error -> LoadErrorに変換される
    let mut de = Deserializer::new(&buf);
    let obj = Deserialize::deserialize(&mut de)?;  // こっちも
    Ok(obj)
}
```

これで`failure::Error`を使った場合と同様のインターフェースで使える(/・ω・)/

## 2017/8/21追記

enum-error-deriveは他のproc_macroを追加するため、v0.2から[procedurals](https://github.com/termoshtt/procedurals)にリネームされました。
enum-error-derive v0.1.1は引き続き利用可能ですが、メンテされません。


## 2018/5/28追記
現在では公式に[failure](https://github.com/rust-lang-nursery/failure)というライブラリがあるのでこちらを使うのがおすすめです

```rust
#[derive(Debug, Fail)]
enum ToolchainError {
    #[fail(display = "invalid toolchain name: {}", name)]
    InvalidToolchainName {
        name: String,
    },
    #[fail(display = "unknown toolchain version: {}", version)]
    UnknownToolchainVersion {
        version: String,
    }
}
```

`Fail` traitが導入されて色々使いやすくなってます(/・ω・)/

## 2018/8/4更新
- failure向けに書き直し
- serde 1.0向けのコードに修正

## 2019/3/10追記
failureでの実験を経て標準ライブラリの`Error`トレイトが拡張されます。詳しくは以下のRFCの和訳を参照してください

- [RFC 2504 "fix_error": Rustの新たなErrorトレイト](https://qiita.com/termoshtt/items/830008898f90c647a971)