プロジェクトで使用するRustツールチェインのバージョンをチームで共有する

[![Rust Programming Language Forum Summary](https://qiita-image-store.s3.amazonaws.com/0/9516/cf4017c6-1072-13b6-66dd-6e33d76b7df1.png)](https://users.rust-lang.org/t/freeze-required-compiler-version-and-toolchain-in-the-cargo-toml/15938/6)
（画像をクリックすると投稿が開きます）

> rustup だとプロジェクト内に `.rust-toolchain` ファイルが置けるよ。するとそのディレクトリでは指定したバージョンの `rustc / cargo` が使われるようになるんだ。

ちなみに正しくは `rust-toolchain` ファイル（先頭のドットなし）

こんな方法があるとは知りませんでした。このことは rustup の[現在の README](https://github.com/rust-lang-nursery/rustup.rs/blob/060192bd8089b4866f1571d85e85ca0264323233/README.md#the-toolchain-file) にしっかり書いてあるのですが、私が README を最後に読んだのはたぶん1年以上前のこと。その頃にはこの機能はなかったような。

早速、使ってみましょう。すでに [rustup で Rust ツールチェインのバージョンを管理している](https://qiita.com/chikoski/items/b6461367e8c3875bb235) 前提で進めます。


## `rust-toolchain` ファイルを使ってみる

Rust で開発していると時々コンパイラのバージョンを固定したくなることがあります。たとえば使用しているクレートが特定の日にビルドされた nightly でないとコンパイルできないような時です。

バージョンを固定する方法としてよく知られているのはプロジェクトのトップディレクトリ（Cargo.tomlのあるディレクトリ）に移動して、`rustup override set <ツールチェインのバージョン>` コマンドを実行することです。でもこの方法だと、特にチームで開発している時などメンバー間で徹底するのが難しそうです。また、クレートのバージョンを上げるたびに全員で `rustup override` しなおすのも非効率です。

`rust-toolchain` ファイルはそんな時に便利に使えそうです。以下のようにプロジェクトのトップディレクトに移動し、`rust-toolchain` ファイルを作ります。

```console
$ rustup -V
rustup 1.11.0 (e751ff9f8 2018-02-13)

# テキトーなプロジェクトを作る。
$ cargo new --bin hello
$ cd hello

# プロジェクトを作ったばかりだとデフォルトのツールチェインが
# アクティブになる。（一番下のactive toolchainのところ）
$ rustup show
Default host: x86_64-unknown-freebsd

installed toolchains
--------------------

stable-x86_64-unknown-freebsd
beta-x86_64-unknown-freebsd
nightly-x86_64-unknown-freebsd
1.9.0-x86_64-unknown-freebsd
1.10.0-x86_64-unknown-freebsd
...
1.23.0-x86_64-unknown-freebsd
1.24.1-x86_64-unknown-freebsd

active toolchain
----------------

stable-x86_64-unknown-freebsd (default)
rustc 1.24.1 (d3ae9a9e0 2018-02-27)

# rust-toolchainファイルを作成し、特定のバージョンに固定する。
$ echo 'nightly-2018-03-07' > rust-toolchain

# cargoやrustcを使おうとするとツールチェインが
# 自動的にダウンロードされる。
$ cargo check
info: syncing channel updates for
  'nightly-2018-03-07-x86_64-unknown-freebsd'
info: latest update on 2018-03-07, 
  rust version 1.26.0-nightly (2789b067d 2018-03-06)
info: downloading component 'rustc'
...
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'cargo'
info: installing component 'rust-docs'
   Compiling hello v0.1.0 (file:///.../hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.14 secs

$ rustup show
...
active toolchain
----------------

nightly-2018-03-07-x86_64-unknown-freebsd (overridden by
  '.../hello/rust-toolchain')
rustc 1.26.0-nightly (2789b067d 2018-03-06)


# 特定のstableリリースに変更する。
$ echo '1.24.1' > rust-toolchain

$ rustup show
...
active toolchain
----------------

1.24.1-x86_64-unknown-freebsd (overridden by
  '.../hello/rust-toolchain')
rustc 1.24.1 (d3ae9a9e0 2018-02-27)
```

このように `rust-toolchain` ファイルを使って Rust ツールチェインのバージョンを指定できました。このファイルを Cargo.lock などと共に他のメンバーと共有すれば、同一の環境を得られます。


## 設定の優先順位

もしすでに `rust-toolchain` ファイルがあるプロジェクトに対して `rustup override set` コマンドを実行したらどうなるでしょうか？ その答えも [README に書かれています。](https://github.com/rust-lang-nursery/rustup.rs/blob/060192bd8089b4866f1571d85e85ca0264323233/README.md#override-precedence)

それぞれの設定は以下の優先順位で使われます（優先度の高いものから低いものへ）

- `cargo +beta` などによる明示的な指定
- `RUSTUP_TOOLCHAIN` 環境変数
- ディレクトリごとの上書き `rustup override set nightly`
- `rust-toolchain` ファイル
- デフォルトのツールチェイン


## ~~Rust Epoch~~　Rust: 2018 Edition

余談ですが、近々、Rust ~~Epoch~~ Edition というしくみが導入されるようです（[RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md)）

（**2018年3月13日修正**：[3月12日のオフィシャルブログの記事](https://blog.rust-lang.org/2018/03/12/roadmap.html)では Epoch ではなく、Edition という呼び名が使われていますので、本記事もそれに合わせて修正しました）

2015年5月に1.0がリリースされて以来、stable 版の Rust は高いレベルで後方互換性を維持してきました。過去に開発したプログラムは、Rust コンパイラを当時より新しいバージョンに変えたとしても、ほとんどの場合で問題なくコンパイル・実行できます（warning は出るかもしれませんが）

その一方で、言語仕様やコンパイラの設計は絶え間なく改良されており、リリースから3年近くたった今では、互換性に影響のある変更・新機能も予定されています。

このような非互換の変更を扱うのが edition です。`rustc` に `--edition`（のような）引数を導入してデフォルトは `2015` にします。デフォルトのまま使っている限り、今まで通り後方互換性が得られます。

非互換の新しい機能を使うためには `--edition 2018` などとして明示的に edition を選択します。また Cargo.toml にも `edition = "2018"` と書けるようして、依存しているクレートのバージョンが選択できるようにしたり、IDE、`rustfmt`、Clippyなどのツール類の振る舞いを変えたりできるようです。

現在の Rust では nightly に頼る場面も多く `rust-toolchain` ファイルによる管理が必要ですが、将来は edition による管理に移行できるかもしれません。

次の edition に入る予定の機能は、[Epoch.Next Tracking Issue #46889](https://github.com/rust-lang/rust/issues/46889) にまとめられています。

- **所有権**：NLLなど（[NLLの日本語による紹介記事](https://ubnt-intrepid.github.io/blog/2017/12/01/rfc-2094-ja/)）
- **トレイトシステム**：`impl Trait` と `dyn Trait`（[日本語による紹介記事](http://qnighy.hatenablog.com/entry/2018/01/28/220000)）、一部のクロージャが `Clone` や `Copy` トレイトを実装、など
- **エラーハンドリング**：Option などの型で `?` が使用可能に、`main()` 関数内で `?` が使用可能に
- **モジュールシステム**：`mod.rs` ファイルが不要になり、`crate` キーワードが追加される、など

Rust の [2018年のロードマップ](https://github.com/rust-lang/rfcs/pull/2314) には上の（`Rust 2018 Edition`）をリリースすることが目標の一つに挙げられています。今年も Rust から目が離せませんね。