Cargoの[dev|build]-dependenciesに関するバグ

## TL;DR
* 現在のstableチャンネルのCargo[^1]では、ルートクレート[^2]の依存パッケージを決定する際に`dev-dependencies`と`build-dependencies`も考慮してしまう。
* nightlyのCargoでは修正されていて、`cargo +nightly -Z features=dev_dep [Command]`や`cargo +nightly -Z features=host_dep [Command]`とすることで回避できる。[^3] [^4]

[^1]: cargo 1.46.0 (149022b1d 2020-07-17)
[^2]: `cargo`コマンドを呼ぶ時にいるディレクトリのプロジェクト。正式な呼称が分からなかったのでこのように呼んでいる。
[^3]: https://github.com/rust-lang/cargo/issues/7915
[^4]: https://github.com/rust-lang/cargo/issues/7916

# バグの紹介
　`Cargo.toml`には`dev-dependencies`という項目を追加でき、`tests`や`examples`などをビルドする際に必要な依存クレートを記すために使われる。この目的から、この項目に記載されているものはライブラリや実行バイナリ本体のビルドには不必要なものである事が容易に分かる（そもそも本体のビルドに必要であれば`dependencies`に記すだけで十分である）。
　しかし、現在のstableチャンネルから得られるCargoはプロダクト本体をビルドする際にこの`dev-dependencies`のビルドに記されている依存クレートも考慮に入れて依存関係の解決をしてしまう。このバグは`build.rs`で必要になる依存クレートを記載するための`build-dependencies`についても同様に発生する。

# 問題が起きるケース
このバグは致命的なエラーを引き起こすことは少ないが、特定の条件ではライブラリのバグを見逃してしまう。例えば開発しているライブラリが`num-traits`に依存していて以下のような項目を含む`Cargo.toml`であるとしよう。

```toml:Cargo.toml
[dependencies]
num-traits = { version = "0.2.12", default-features = false }

[dev-dependencies]
num-traits = { version = "0.2.12", features = ["std"] }
```

`num-traits`はデフォルトで`std`フィーチャーが有効になっているが、ここでは`default-features = false`とすることによって無効化している。一方、`dev-dependencies`では`std`フィーチャーが必要な場合を想定している。

ここで、`num-traits`の`Float`トレイトをインポートしてみる。

```rust:src/lib.rs
#[allow(unused_imports)]
use num_traits::float::Float;
```

実は`Float`トレイトは`num-traits`の`std`フィーチャーが有効化されている時のみ定義されているので、このコードはビルドに失敗して然るべきである。しかしCargoのバグによりこのライブラリをビルドする際には`dev-dependencies`が考慮されるので、`num-traits`は`std`フィーチャーが有効化された状態でビルドされ、このライブラリはビルドが成功してしまう。しかし、このライブラリに依存するクレートをビルドしようとした時には問題が起こる。

このライブラリの名前を`dev_dep_bug`として、このクレートと同じディレクトリに、`dev_dep_bug`に依存するバイナリクレートを新たに作ろう。

```toml:Cargo.toml
[dependencies]
dev_dep_bug = { version = "*", path = "../dev_dep_bug"}
```

依存クレートをビルドする際は、依存クレートの`dev-dependencies`は考慮されないので`num-traits`を`std`フィーチャーが無効化された状態でビルドする。これによって`dev_dep_bug`をビルドする際に`Float`トレイトが見つからず、以下のようなエラーが吐き出される。

```console
$ cargo check
    Updating crates.io index
   Compiling autocfg v1.0.1
   Compiling num-traits v0.2.12
    Checking dev_dep_bug v0.1.0 (/path/to/dev_dep_bug)
error[E0432]: unresolved import `num_traits::float::Float`
 --> /path/to/dev_dep_bug/src/lib.rs:2:5
  |
2 | use num_traits::float::Float;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ no `Float` in `float`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `dev_dep_bug`.

To learn more, run the command again with --verbose.
```

この問題はライブラリ使用者が手の付けられない範囲に原因が潜んでおり、IssueやPRを投げる他に対策方法が無いことにある（forkしたり手元にライブラリのコードを持ってきて修正したものを使用するという手があるが、そこまでやるならPRを投げてContributeした方が良いと思う）。
しかもライブラリ開発者は手元であれCIであれ、単純に`cargo check`や`cargo build`をするだけでは気づき得ないという点が凶悪である。

# 解決方法
このバグはnightlyチャンネルでは既に修正されており、`-Z`フラグを使って`features=dev_dep`[^3]や`features=host_dep`[^4]を指定することによってそれぞれ`dev-dependencies`、`build-dependencies`のバグを回避でき、ライブラリがこれに起因する依存関係のバグを仕込んでいないか確認が取れる。

```console
$ cargo +nightly -Zfeatures=dev_dep check # or build
$ cargo +nightly -Zfeatures=host_dep check # or build
```

最後に先程の例をnightlyのCargoでビルドしてみると、以下の通り正しくビルドエラーになる。

```console
$ cargo +nightly -Zfeatures=dev_dep check
    Checking dev_dep_bug v0.1.0 (/path/to/dev_dep_bug)
error[E0432]: unresolved import `num_traits::float::Float`
 --> src/lib.rs:2:5
  |
2 | use num_traits::float::Float;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ no `Float` in `float`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `dev_dep_bug`.

To learn more, run the command again with --verbose.
```