Rustで std::io::Error をcloneしたいとき

# `std::io::Error` を複製したい
[`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)構造体は `Clone` trait を実装していないため、通常の手段では複製できない。
これが気になるのが、以下のようなケースだ。

```rust:自作エラー型の例
/// ファイルになにかする系の自作ライブラリが出すエラー
#[derive(Debug)]
pub enum FileOpError {
    CancelledByUser,
    Io(std::io::Error),
    LockedData,
}
```

ここで、自前で定義した `FileOpError` に `Clone` を自動で実装させたくとも、それができないのである。
では、どうしてもこいつを Clone させたい場合にどうするか。

以下のようにすれば良い。

```rust:cloneの手動実装
impl Clone for FileOpError {
    fn clone(&self) -> Self {
        match *self {
            FileOpError::CancelledByUser => FileOpError::CancelledByUser,
            FileOpError::Io(ref err) => FileOpError::Io(std::io::Error::new(err.kind(), err.to_string())),
            FileOpError::LockedData => FileOpError::LockedData,
        }
    }
}
```

# `std::io::Error` の複製を自前で構築

唯一説明が必要であろう部分がこれだ。

```rust:IOエラーを複製する
std::io::Error::new(
    err.kind(),
    err.to_string())
```

[`std::io::Error::new()`](https://doc.rust-lang.org/std/io/struct.Error.html#method.new)は、[`std::io::ErrorKind`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html)と別の [`Error`](https://doc.rust-lang.org/std/error/trait.Error.html) trait を実装したオブジェクトから、新たに `std::io::Error` を作る。

`ErrorKind` は、[`std::io::Error::kind()`](https://doc.rust-lang.org/std/io/struct.Error.html#method.kind)で得られ、 `Clone` と `Copy` trait が実装されている。

## inner error

`io::Error::new()` のもうひとつの引数であるが、そもそもこれは**I/Oエラーの原因となったエラー**、あるいは**より低レベルで詳細を説明するエラー**(これを **inner error** と呼ぶ)を渡すものだ。(逆にこれを `io::Error` から得るには、 [`std::io::Error::into_inner()`](https://doc.rust-lang.org/std/io/struct.Error.html#method.into_inner)等を使う。)
しかし、元のエラーを consume せずに inner error を得ても、この場面での利用は難しい(`&Error` までは取れるが、そこから `Box<Error>` を作れない)。
よって、本来の inner error の利用を諦め、代わりに**文字列をエラー情報として持たせて**やれば良い。
エラーを説明する文字列の取得は ~~[`std::io::Error::description()`](https://doc.rust-lang.org/std/error/trait.Error.html#tymethod.description)~~ `.to_string()` で行える。

追記 2018-06-19: `Error::description()` は非推奨となり、 `Display` トレイトや、 `Display` を実装している型に対して間接的に自動で用意される [`ToString::to_string()`](https://doc.rust-lang.org/1.48.0/std/string/trait.ToString.html#tymethod.to_string) を使いましょうということになっている。

### 参考: 文字列の lifetime と、エラーとしての利用

`std::io::Error::new()` が受け取るのは `Into<Box<Error + Send + Sync>>` である。
そして[`std::convert::Into`](https://doc.rust-lang.org/std/convert/trait.Into.html)によれば、 "`From<T> for U` implies `Into<U> for T`" であり、[`std::convert::From`](https://doc.rust-lang.org/std/convert/trait.From.html)によれば `impl From<String> for Box<Error + Send + Sync>` が実装されているから、結果として `String` を `Box<Error + Send + Sync>` に変換できるというわけである。

# 追記(2016/10/15): `Rc` や `Arc` を使う方法
`io::Error` の `Clone` についてはIssueが立ってはいる([Consider changing io::Error to use Arc so it can implement Clone · Issue #24135 · rust-lang/rust](https://github.com/rust-lang/rust/issues/24135))が、進展のあった様子はない。
ここで言及されていることには、 `std::io::Error` は(inner errorも共に) `Sync` と `Send` traitを実装している。
すなわち、inner errorも `io::Error` も共に[`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)を使って管理できるということである。
よって、直接に `io::Error` を持たず、その `Arc` を持てば、その `Clone` を使うことができる。

```rust:自作エラー型の例(コンパイルが通る)
/// ファイルになにかする系の自作ライブラリが出すエラー
#[derive(Debug, Clone)]
pub enum FileOpError {
    CancelledByUser,
    Io(std::sync::Arc<std::io::Error>),  // `Arc` を使う
    LockedData,
}
```

マルチスレッドを全く考えないのであれば、勿論 `Arc` の代わりに `std::rc::Rc` を使っても良いだろう。

# まとめ

- 自前で `Clone` もどきを実装するなら:
  - `io::Error::new()` を使う。
  - エラーの種類は `io::Error::kind()` で得る。
  - エラーの内容は完全には複製できないので、代わりに `format!()` や `.to_string()` で得た文字列を使う。
- `Rc` や `Arc` を使って良いなら:
  - `Arc<io::Error>` のようにすれば、 `Rc` や `Arc` の `Clone` をそのまま使える。

以上。