RFC 2504 "fix_error": Rustの新たなErrorトレイト

この記事はRustの[RFC2504 "fix_error"](https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md)の和訳です。現在はRFCが採用され、nightlyに実装が進んでいる状態です（なのでまだ動く環境はない）。

途中で登場する`#[non_exhaustive]`についてはこちら：[Non-exhaustive enum/struct](https://qiita.com/termoshtt/items/b224b95f666ce8560d58)

以下翻訳

---------------------

- Feature Name: `fix_error`
- Start Date: 2018-07-18
- RFC PR: [rust-lang/rfcs#2504](https://github.com/rust-lang/rfcs/pull/2504)
- Rust Issue: [rust-lang/rust#53487](https://github.com/rust-lang/rust/issues/53487)

# Summary
[summary]: #summary

<!--
Change the `std::error::Error` trait to improve its usability. Introduce a
backtrace module to the standard library to provide a standard interface for
backtraces.
-->

`std::error::Error`トレイトを使いやすくするために変更します。`backtrace`モジュールを標準ライブラリに追加し、バックトレースにアクセスするための標準的なインターフェースを導入します

# Motivation
[motivation]: #motivation

<!--
The `Error` trait has long been known to be flawed in several respects. In
particular:
-->
`Error`トレイトは長い間、いくつかの点で欠陥が指摘されていました：

<!--
1. The required `description` method is limited, usually, to returning static
strings, and has little utility that isn't adequately served by the required
`Display` impl for the error type.
2. The signature of the `cause` method does not allow the user to downcast
the cause type, limiting the utility of that method unnecessarily.
3. It provides no standard API for errors that contain backtraces (as some
users' errors do) to expose their backtraces to end users.
-->

1. `description`メソッドの機能が限られており、通常静的な文字列を返すだけでエラー型の`Display`の実装のために十分ではありませんでした
2. `cause`メソッドの定義が、ユーザーに原因の型へダウンキャストを不必要に妨げています
3. 標準となるバックトレースAPIをユーザーに提供していません

<!--
We propose to fix this by deprecating the existing methods of `Error` and adding
two new, provided methods. As a result, the undeprecated portion of the `Error`
trait would look like this:
-->

このRFCでは`Error`のこれらのメソッドを非推奨(deprecated)とし、新たに二つのメソッドを追加します。その結果`Error`トレイトは次のようになります：

```rust
trait Error: Display + Debug {
    fn backtrace(&self) -> Option<&Backtrace> {
        None
    }

    fn source(&self) -> Option<&dyn Error + 'static> {
        None
    }
}
```

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

<!--
## The new API of the Error trait
-->

## Errorトレイトの新たなAPI

<!--
The new API provides three main components:
-->

新しいAPIは主に次の３つから構成されます：

<!--
1. The Display and Debug impls, for printing error messages. Ideally, the
   Display API would be focused on *end user* messages, whereas the Debug impl
   would contain information relevant to the programmer.
2. The backtrace method. If the Error contains a backtrace, it should be exposed
   through this method. Errors are not required to contain a backtrace, and are
   not expected to.
3. The `source` method. This returns another Error type, which is the underlying
   source of this error. If this error has no underlying source (that is, it is
   the "root source" of the error), this method should return none.
-->

1. `Display`, `Debug`の実装をエラーメッセージを表示するために用います。理想的には`Display`は*エンドユーザー*に向けたメッセージに、`Debug`はプログラマに向けたメッセージになります。
2. `backtrace`メソッドが追加されます。エラーがバックトレースを保持している場合、このメソッドを通して取得できるはずです。エラー型がバックトレースを保持する事は必須ではありません。
3. `source`メソッドが追加されます。これはエラーの原因となった別のエラー型を返します。もしエラーが原因となった他のエラーを持たない場合このメソッドは`None`を返します。

## The backtrace API

<!--
This RFC adds a new `backtrace` module to std, with one type, with this API:
-->
このRFCによって`backtrace`モジュールが新たに標準ライブラリに追加されます：

```rust
pub struct Backtrace {
    // ...
}

impl Backtrace {
    // プラットフォームがサポートされている場合、現在のスタックのバックトレースを取得します
    //
    // この関数は環境変数の値を参照します
    pub fn capture() -> Backtrace {
        // ...
    }

    // プラットフォームがサポートされている場合、現在のスタックのバックトレースを取得します
    //
    // この関数は環境変数の値を無視します
    pub fn force_capture() -> Backtrace {
        // ...
    }

    pub fn status(&self) -> BacktraceStatus {
        // ...
    }
}

impl Display for Backtrace {
    // ...
}

impl Debug for Backtrace {
    // ...
}

#[non_exhaustive]
pub enum BacktraceStatus {
    Unsupported,
    Disabled,
    Captured
}
```

<!--
This minimal initial API is just intended for printing backtraces for end users.
In time, this may grow the ability to visit individual frames of the backtrace.
-->

この最小の初期APIはエンドユーザーにバックトレースを表示することを目的としてます。そのうちバックトレースの個々のフレームを表示できたりするようになるでしょう。

<!--
### Backtrace controlling environment variables
-->

### バックトレースを制御する環境変数

<!--
Today, the `RUST_BACKTRACE` controls backtraces generated by panics. After this
RFC, it also controls backtraces generated in the standard library: no backtrace
will be generated when calling `Backtrace::capture` unless this variable is set.
On the other hand, `Backtrace::force_capture` will ignore this variable.
-->
現行`RUST_BACKTRACE`環境変数がパニック時のバックトレースの生成を制御していますが、このRFC後は標準ライブラリでのバックトレースの生成も制御するようになります：`Backtrace::capture`はこの環境変数がセットされていない限りバックトレースを生成しませんが、`Backtrace::force_capture`はこの環境変数を無視します。

<!--
Two additional variables will be added: `RUST_PANIC_BACKTRACE` and
`RUST_LIB_BACKTRACE`: these will independently override the behavior of
`RUST_BACKTRACE` for backtraces generated for panics and from the std API.
-->
加えてパニック時と標準ライブラリ経由の場合を独立に制御するために二つの変数、`RUST_PANIC_BACKTRACE`と`RUST_LIB_BACKTRACE`が追加され、それぞれ独立に`RUST_BACKTRACE`より優先されます。

<!--
## The transition plan
-->
## 移行プラン

<!--
Deprecating both `cause` and `description` is a backward compatible change, and
adding provided methods `backtrace` and `source` is also backward compatible.
We can make these changes unceremoniously, and the `Error` trait will be much
more functional.
-->

`cause`と`description`の非推奨への変更、及び`backtrace`と`source`メソッドの追加は後方互換があります。これらの変更は（訳注Rust2018のように）儀式ばらずに実行でき、`Error`トレイトはより機能的になります。

<!--
We also change the default definition of `cause`, even though it is deprecated:
-->
`cause`のデフォルト実装は、このメソッドは非推奨になりますが、次のように変更されます：

```rust
fn cause(&self) -> Option<&dyn Error> {
    self.source()
}
```

<!--
This way, if an error defines `source`, someone calling the deprecated `cause`
API will still get the correct cause type, even though they can't downcast it.
-->

これにより、もしエラー型が`source`を定義しているのに誰かが非推奨の`cause`を呼び出したとしても正しく原因となったエラー型を返すことが出来ます。ただし元の型にダウンキャストすることはできません。

<!--
## Stability
-->
## 安定化

<!--
The addition of `source` and the deprecation of `cause` will be instantly
stabilized after implementing this RFC.
-->
`source`の追加と`cause`の非推奨化はRFCの実装直後の安定化されます。

<!--
The addition of the backtrace method and the entire backtrace API will be left
unstable under the `backtrace` feature for now.
-->
`backtrace`メソッドの追加とバックトレースAPIの追加は`backtrace` featureとしてしばらくunstableになるでしょう。

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

## Why `cause` -> `source`

<!--
The problem with the existing `cause` API is that the error it returns is not
`'static`. This means it is not possible to downcast the error trait object,
because downcasting can only be done on `'static` trait objects (for soundness
reasons).
-->

既存の`cause`APIの問題は返されるエラー型が`'static`でない事でした。健全性の理由から`'static`でないトレイトオブジェクトはダウンキャストが出来ないので、これではエラー型のトレイトオブジェクトがダウンキャストできません。

<!--
## Note on backtraces
-->

## バックトレースに関する補足

<!--
The behavior of backtraces is somewhat platform specific, and on certain
platforms backtraces may contain strange and inaccurate information. The
backtraces provided by the standard library are designed for user display
purposes only, and not guaranteed to provide a perfect representation of the
program state, depending on the capabilities of the platform.
-->

バックトレースの挙動はプラットフォームにいくらか依存しており、一部のプラットフォームではバックトレースは不正確な情報を含んでいることがあります。
プラットフォームに応じて、標準ライブラリによって提供されるバックトレースはユーザーに表示することを目的としており、プログラムの状態に対する完全な表現を保証しません。

## How this impacts failure

<!--
The failure crate defines a `Fail` trait with an API similar to (but not
exactly like) the API proposed here. In a breaking change to failure, we would
change that trait to be an extension trait to `Error`:
-->
`failure` crateは`Fail`トレイトによく似た（しかし正確には一致しない）APIを定義しています。
`failure`における破壊的変更になりますが、`Fail`は`Error`を拡張するトレイトになるでしょう。

```rust
// Maybe rename to ErrorExt?
trait Fail: Error + Send + Sync + 'static {
    // various provided helper methods
}

impl<E: Error + Send + Sync + 'static> Fail for E {

}
```
<!--
Instead of providing a derive for Fail, failure would provide a derive for the
std library Error trait, e.g.:
-->

`Fail`のderiveを提供する代わりに、`failure`は標準ライブラリの`Error`に対する次のようなderiveを提供することになるでしょう：

```rust
#[derive(Debug, Display, Error)]
#[display = "My display message."]
struct MyError {
    #[error(source)]
    underlying: io::Error,
    backtrace: Backtrace,
}
```

<!--
The exact nature of the new failure API would be determined by the maintainers
of failure, it would not be proscribed by this RFC. This section is just to
demonstrate that failure could still work using the std Error trait.
-->
`failure`の新しいAPIの詳細は`failure`のメンテナによって決定されるでしょうが、このRFCによって`failure`が追放されるわけではありません。
ここでは`failure`が標準ライブラリの`Error`と協働できることを示しているにすぎません。

# Drawbacks
[drawbacks]: #drawbacks

<!--
This causes some churn, as users who are already using one of the deprecated
methods will be encouraged (by warnings) to change their code, and library
authors will need to revisit whether they should override one of the new
methods.
-->
既に非推奨のメソッドを使用しているユーザーに対して（コンパイラの警告で）移行を推奨し、ライブラリの作者に対して再び新たなメソッドをオーバーライドするかどうかの判断をせまることになる

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

## Provide a new error trait

<!--
The most obvious alternative to this RFC would be to provide an entirely new
error trait. This could make deeper changes to the API of the trait than we are
making here. For example, we could take an approach like `failure` has, and
impose stricter bounds on all implementations of the trait:
-->

明らかな別案として、全く新しいトレイトを導入する案がある。これはこのRFCで導入される方法よりも深い変更が必要となる。
例えば`failure`が採用しているのと似た方法をとり、より強い制約を全ての実装に対して課す：

```rust
trait Fail: Display + Debug + Send + Sync + 'static {
    fn cause(&self) -> Option<&dyn Fail> {
        None
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        None
    }
}
```

<!--
Doing this would allow us to assemble a more perfect error trait, rather than
limiting us to the changes we can make backwards compatibly to the existing
trait.
-->
これで既存のトレイトとの後方互換性に縛られることなく完全なエラートレイトを実装することが可能となるだろう。

<!--
However, it would be much more disruptive to the ecosystem than changing the
existing trait. We've already seen some friction with failure and other APIs
(like serde's) that expect to receive something that implements `Error`. Right
now, we reason that the churn is not worth slight improvements that wouldn't be
compatible with the Error trait as it exists.
-->
しかしながら、これはエコシステムにとって既存のトレイトを変更するよりもはるかに破壊的なものになる。
既に`failure`と（例えば`serde`のように）`Error`を受け取るAPIとの不整合に遭遇しており、現行のトレイトとの不整合を解消するほんの少しの改善の為に今この変更をとる価値はないと判断した。

<!--
In the future, if these changes are not enough to resolve the warts with the
Error trait, we could follow this alternative: we would deprecate the Error
trait and introduce a new trait then. That is, accepting this RFC now does not
foreclose on this alternative later.
-->
もし将来、エラートレイトの問題点をこのRFCが十分に解決できないとわかったらこの代替案に切り替えるでしょう。

## Bikeshedding the name of `source`

<!--
The replacement for `cause` could have another name. The only one the RFC author
came up with is `origin`.
-->
`cause`の他の名称として著者が思いつくのは`origin`である。

# Prior art
[prior-art]: #prior-art

<!--
This proposal is largely informed by our experience with the existing Error
trait API, and from the experience of the `failure` experiment. The main
conclusions we drew:
-->
この提案は既存のErrorトレイトのAPIと`failure`の実験での経験を基にしている。主要な結論としては：

<!--
1. The current Error trait has serious flaws.
2. The `Fail` trait in failure has a better API.
3. Having multiple error traits in the ecosystem (e.g. both `Error` and `Fail`)
can be very disruptive.
-->

1. 現在の`Error`トレイトには重大な欠陥がある
2. failureの`Fail`トレイトはより良いAPIになっている
3. 複数のエラートレイトがあるのはエコシステムにとって混乱のもとである

<!--
This RFC threads the needle between the problem with the Error trait and the
problems caused by defining a new trait.
-->
このRFCはエラートレイトの問題と新たなトレイトの導入を上手く解決するものになる

# Unresolved questions
[unresolved-questions]: #unresolved-questions

## Backtrace API

<!--
This RFC intentionally proposes a most minimal API. There are a number of API
extensions we could consider in the future. Prominent examples:
-->
このRFCでは意図的に最小のAPIを提案しており、将来的に多くの拡張を考えています。主要な例としては：

<!--
1. Extending the backtrace API to allow programmatic iteration of backtrace
frames and so on.
2. Providing derives for traits like `Display` and `Error` in the standard
libray.
3. Providing helper methods on `Error` that have been experimented with in
failure, such as the causes iterator.
-->
1. プログラミング可能なバックトレースのイテレーションに対するバックトレースAPIの拡張
2. 標準ライブラリでの`Display`と`Error`に対するderive
3. `Error`に`failure`で試験されている機能、例えば`cause iterator`に対する補助メソッドを提供する

<!--
None of these are proposed as a part of *this* RFC, and would have a future RFC
discussion.
-->
これらは**この**RFCには含まれないが、将来のRFCで議論されるでしょう。

<!--
Additionally, the choice to implement nullability internal to backtrace may
prove to be a mistake: during the period when backtrace APIs are only available
on nightly, we will gain more experience and possible change backtrace's
constructors to return an `Option<Backtrace>` instead.
-->
さらに言えば、`backtrace`の内部的なnullabilityの設計は間違いと分かるかもしれない：
バックトレースAPIがnightlyのみで使える実装期間に得た経験によっては再び`Option<Backtrace>`に戻すかもしれない。