[Rust]自作Errorを作る

# 前回の記事
前回「[[Rust]自作Errorを作りたくて`std::io::Error`を読んでみた！](https://qiita.com/mutuya/items/5b60c5c749b4a234c545)」という記事を書きました。

今回は実際に作って行こうと思います。実践編って感じです。

一応ですが、これは**私のやり方**であって、正解というわけでも、どこかの誰かが推奨しているわけでもありません。なのでもっといいやり方は自分で開発してくださいな。

# 課題１
エラー内容（`&'static str`）を表すError型を作成すること。

また、この型が表すエラーは他のエラーから連鎖的に発生する可能性があるものとする。

## Step1；「他のエラーに誘起されるか」
答えはYesです。連鎖的に発生する可能性があるのですから。。

ということで、自作Errorには、他のエラーを入れれるようにしないといけません。ですが誘起されてではなく、自然発生（？）することもありえそうです。

そこで列挙型を用いて次のようにします。

```rust
use std::error;

pub struct Error {
    _error: _Error,
}

impl Error {
    // 他のエラー型を受け取れるようにコンストラクタを作っておく
    fn new<E>(kind: ?, error: E) -> Self
        where
            E: Into<Box<dyn error::Error + Send + Sync>>
    {
        Error{ _error: _Error::Custom((kind, error.into())) }
    }
}

enum _Error {
    Simple( ? ),
    Custom( (?, Box<dyn error::Error + Send + Sync>) ),
}
```

将来、突っ込まれるであろう他のエラーの型なんぞ、さっぱりわかりません。しかしエラーを表す型なら`Box<dyn std::error::Error + Send + Sync>`に`Into`できます（詳細は[前回の記事](https://qiita.com/mutuya/items/5b60c5c749b4a234c545)へ）。そのためタプルを用いて、上記のようにします。

### 補足：構造体で作るメリット
ここでは構造体で列挙型を包む形を取っています。なぜかというと**中身を見せないため**です。

次のように定義したと仮定しましょう。

```rust
pub enum Error {
    Simple( ? ),
    Custom( (?, Box<dyn std::error::Error + Send + Sync>) ),
}
```

これでも上手く自作Errorが作れそうですよね？

しかし、これだと問題があります。それはPublicな列挙型は**中身が見える**ということです。

つまり、誰かが`Error::Simple(ほにゃほにゃ)`して予期しない使い方をされたり、隠蔽できてないことから使い方を誤解させたりしかねません。

そのため**中身を見せないように、ひと手間踏んで、隠蔽してる**のです。

## Step2；「エラーはどんな種類があるか？」
今回の課題でははっきりしてます。`&'static str`オンリーです。

ということで、先程の`?`を`&'static str`で埋めます。

```rust
enum _Error {
    Simple( &'static str ),
    Custom( (&'static str, Box<dyn error::Error + Send + Sync>) ),
}
```

はい！ほぼほぼ完成！あとは`error::Error`や`Display`、`Debug`を実装すればいいだけ！

## Step3；「必要なトレイトを実装していく」
まず`Display`と`Debug`から。今回は文字列を内部に持っているので、どちらも、それを表示させたいと思います。

```rust
use std::fmt;

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self._error {
            _Error::Simple(s) => f.write_str(s),
            _Error::Custom(c) => f.write_str(c.0),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Self as fmt::Display>::fmt(self, f)
    }
}
```


私は「この後に実装する`source`メソッドで、発生元のエラーを取得できるのだから、このエラーの内容を表示させればよい」と考えこうしています。

しかし、連鎖元の内容を出力させたいとか、もっと意外性にとんだ人を魅了するデバック出力がしたいという人とかがいるかと思います。

そういう人は適宜書き換えましょう。大切なのはデバックしやすいということですから。

```rust
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self._error {
            _Error::Simple(s) => f.write_str(s),
            _Error::Custom(c) => c.1.fmt(f),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use std::f64::consts::PI;
        f.write_fmt(format_args!("{}", (PI / PI) ))
    }
}
```

次に`error::Error`トレイトを実装します。今回は連鎖してくる可能性があるので`source`メソッドを忘れずに書いておきます。

```rust
use std::error;

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match &self._error {
            _Error::Simple(_) => None,
            _Error::Custom(c) => c.1.source(),
        }
    }
}
```

## Step4；「他の必要なメソッド等を書く」
最後に他のメソッドも実装して終わりです。コンストラクタとか。

コンストラクタであれば`From<&'static str>`とかでさくっと適宜つくっちゃってくださいな。

# 課題2
図書館の蔵書の検索システムに使われるError型を作ると想定します。

基本的には「蔵書を発見できなかった」、「蔵書を発見できたが利用不可能な状態だった」、「利用者には知る権利がなかった」の3種類のエラーに対応しなければならないとします。

またシステム内のあらゆるところで使われる可能性があるため、他のエラーに誘起される可能性があるとします。

## Step1；「他のエラーに誘起されるか」
課題に書いてあるとおりです。Yesです。

コードは課題1のStep1と同じなため省略します。

## Step2；「エラーはどんな種類があるか？」
「蔵書を発見できなかった」、「蔵書を発見できたが利用不可能な状態だった」、「利用者には知る権利がなかった」の3種類ですね。

それぞれ「NotFound」、「NotAvailable」、「PermissionDenied」として次のように列挙型でコードします。

ついでに`?`のところも、これで埋めます。

```rust
use std::error;

#[derive(Clone)]
pub enum ErrorKind {
    NotFound,
    NotAvailable,
    PermissionDenied
}

pub struct Error {
    _error: _Error,
}

impl Error {
    fn new<E>(kind: ErrorKind, error: E) -> Self
        where
            E: Into<Box<dyn error::Error + Send + Sync>>
    {
        Error{ _error: _Error::Custom((kind, error.into())) }
    }
}

enum _Error {
    Simple( ErrorKind ),
    Custom( (ErrorKind, Box<dyn error::Error + Send + Sync>) ),
}
```


## Step2.5；「ErrorKindに説明をつける」
`Display`や`Debug`のメソッドに`ErrorKind`の説明を書き込んでもダメとはいいませんが、、、おすすめはしません。

`ErrorKind`に説明を返してもらうためのメソッドをつけたほうが後々、書き換える必要が出たときなど楽です。

そこでStep3に行く前に`ErrorKind`にちょっとした実装をしておきます。

```rust
impl ErrorKind {
    pub fn description(&self) -> &'static str {
        match self {
            ErrorKind::NotFound => "蔵書が見つかりませんでした。",
            ErrorKind::NotAvailable => "蔵書は利用可能な状態でありません。",
            ErrorKind::PermissionDenied => "あなたのセキュリティクリアランスでは開示されていません。"
        }
    }
}
```

また`自作Error`から中身の`ErrorKind`を取り出すことは多々ありますので

```rust
impl Error {
    fn kind(&self) -> ErrorKind {
        match &self._error {
            Simple(s) => s.clone(),
            Custom(c) => c.0.clone(),
        }
    }
}
```

も実装すると、後々ちょっと楽です。しかし、今は試しに放置しておきます。

## Step3；「必要なトレイトを実装していく」
Step2.5でここはかなり簡単になります。実際は`Debug`と`Display`で表示内容を変えたりする必要もあることもありますが、今回は無視して同じの使います。

```rust
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self._error {
            _Error::Simple(s) => f.write_str(s.description()),
            _Error::Custom(c) => f.write_str(c.0.description()),
        }
        //
        // kind()があったら
        //     f.write_str(self.kind().description())
        // の１文で済んでいた。
        //
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Self as fmt::Display>::fmt(self, f)
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match &self._error {
            _Error::Simple(_) => None,
            _Error::Custom(c) => c.1.source()
        }
    }
}
```

こんな感じで、必要最低限のトレイトは実装し終えました。

## Step4；「他の必要なメソッド等を書く」
課題1と同様に必要な他のメソッドを追加して終わりです。

お疲れ様でした。