cargo-atcoder を使って Rust での AtCoder ライフを超快適にする

# 追記(2020/08/10)
このエントリで紹介している `cargo-atcoder` の進化版として、@qryxip さんにより、`cargo-compete` が開発されました。
↓↓↓詳しくはご本人による紹介エントリをご覧ください！↓↓↓
[cargo-atcoderの代替品を作った - Qiita](https://qiita.com/qryxip/items/bff57848ac9310d27f1a)

（追記終わり）

Rust で競技プログラミングをやっている方であれば、
[Rustで競技プログラミングの入力をスッキリ記述するマクロ - Qiita](https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8)
で紹介されている `input!` マクロのことを見聞きしたことがある方が多いのではないかと思います。競プロにおいて第一級市民である C++ に比べて標準入力からの受け取りが冗長になりがちな Rust において、さまざまな入力形式に対応する汎用性と記述の短さをもつ `input!` マクロがもたらす恩恵はとても大きなものです。

さて、このマクロを作成した @tanakh さんによって、Rust での AtCoder ライフを超絶快適にするツールが開発されています。その名は **cargo-atcoder**

このエントリでは、**cargo-atcoder** の導入方法や使い方について紹介し、Rust で AtCoder に参加していらっしゃる方々の競プロライフを豊かにする手助けができればなと思います。

なお、このエントリで紹介する内容はほとんど GitHub の README に書かれていますので、一次ソースを確認したい方はそちらをぜひご覧ください。
↓リポジトリへのリンク↓
[tanakh/cargo-atcoder: Cargo subcommand for AtCoder](https://github.com/tanakh/cargo-atcoder)

【更新履歴】


- 2020/08/10 次世代ツール `cargo-compete` の紹介エントリへのリンクを貼りました。
- 2020/06/06 `Cargo.toml` に書く dependencies を欲張りすぎるとビルド時間が長くなって大変だという旨の記述を追加しました。
- 2020/05/10 インストール方法を変更しました （crates.io に公開されたため）

# cargo-atcoder とは

@tanakh さんによって作成された、Rust での AtCoder ライフを豊かにするためのツールです。
豊かにするというのは、具体的には以下のようなことができます。

- `Cargo.toml` や 提出コードの雛形を決めておけば、コンテスト名を指定するだけでコンテスト用のプロジェクトディレクトリが作成できます。
  - 例: `cargo atcoder new abc164` （ABC164 のためのディレクトリを作成）
- 書いたコードがテストケースをパスするかどうかをコマンド一発で確認できます。
  - 例: `$ cargo atcoder test a` （A問題のテストケースを確認）
  ![Apr-27-2020 22-15-04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151210/058ba3e4-cab0-c3de-b05e-af9980e25821.gif)
- 解答の提出がコマンド一発でできます。 
  - 例: `$ cargo atcoder submit a` （A問題のコードを提出）
  <img width="617" alt="スクリーンショット 2020-04-27 22.36.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151210/d383a293-547c-3169-5f72-55f857e30f84.png">
- 提出結果をコマンドで習得できます。リアルタイム更新です。
  - 例: `$ cargo atcoder status`

# 導入方法
## インストール

もし `cargo` が入っていない場合は、何よりもまずインストールしてください。
以下のページにあるコマンドをコピペして実行すればインストールできます。
[rustup.rs - The Rust toolchain installer](https://rustup.rs/)

`cargo` が入ったら以下のコマンドを実行すればOKです。ビルドをするので結構時間かかります。コーヒーでも飲みながら気長にお待ちください。（手元の環境では3分ほどかかりました）

```sh
$ cargo install cargo-atcoder
```

インストールが完了したら、以下のコマンドを打ってみてください。コマンドの説明がずらずら〜と出てきたらOKです。

```sh
$ cargo atcoder --help
cargo-atcoder 0.1.0

USAGE:
    cargo atcoder <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

（以下略）
```

## セットアップ

### ログインする

まず、AtCoder へのログイン情報 (Cookie) をローカルに保存する必要があります。
以下のコマンドを実行すると、ユーザー名とパスワードを聞かれるので、指示通り入力してください。

```sh
$ cargo atcoder login
```

成功すると `Login succeeded.` と表示されます。
（ちなみに Cookie の有効期限は180日間のようです。）

### 設定ファイルを用意する

各プロジェクトの雛形を設定ファイルに書く必要があります。
設定ファイルの場所は OS や設定している環境変数ごとに異なるので、下記を参考にしてください。

|OS | 設定ファイルの場所                                 | 例                          |
| ------- | ------------------------------------- | -------------------------------- |
| Linux   | **$XDG_CONFIG_HOME/cargo-atcoder.toml** または **$HOME/.config/cargo-atcoder.toml** | /home/maguro/.config/cargo-atcoder.toml              |
| macOS   | **$HOME/Library/Preferences/cargo-atcoder.toml**           | /Users/maguro/Library/Preferences/cargo-atcoder.toml |
| Windows | **{FOLDERID_RoamingAppData}\cargo-atcoder.toml**           | C:\Users\maguro\AppData\Roaming\cargo-atcoder.toml   |

ログイン済みであればすでに上記の場所に `cargo-atcoder.toml` が作成されているはずですので、中身を覗いてみてください。
いくつかの項目について簡単に説明します。

#### submit_via_binary

闇の力です。Rust の真のパワーを発揮するべく、**ローカルでビルドし、バイナリでの提出を行います**。
つまり、このオプションが `true` にセットされている場合、ローカルでビルドして生成された実行バイナリを提出して、ジャッジサーバー上ではそのバイナリが実行されます。
**ジャッジサーバー上の実行環境に関係なく、最新バージョン、nightly バージョンの Rust を使ったり、好きなクレートを使ったりすることができます**。

闇の力の代償として、

- 提出ファイルサイズが極めて大きくなる（設定にもよりますが、ABC の A問題程度のコード量であっても、300KB を超えます）
- 実行時のオーバーヘッドが発生する（最低でも 4ms の実行時間がかかる）
- 手元でビルドするので、提出までに少し時間がかかる

といったデメリットがあります。しかし、メリットの大きさはデメリットを補って余りあるものでしょう。

#### use_cross

ジャッジサーバーは Linux 環境です。上記の闇の力を使用してバイナリ提出を行う場合、Linux 向けの実行バイナリを生成する必要があります。
`use_cross` オプションを `true` にしておくと、`cross` という設定要らずのクロスコンパイル環境を使ってコンパイルが行われるようになります。
お使いの環境が Linux ではない場合、とりあえず `true` にしておくほうが面倒事が少なくて良いかなと思います。
なお、`cross` を使うのに `Docker` が必要となるので、事前にご準備ください。

↓`cross` のリポジトリ↓
[rust-embedded/cross: “Zero setup” cross compilation and “cross testing” of Rust crates](https://github.com/rust-embedded/cross)

#### strip_path

バイナリで提出をする際、ファイルサイズを減らすために `strip` というコマンドで処理されます。その `strip` コマンドへのパスを指定するためのオプションです。
PATH が通った場所に `strip` がある場合は特に指定する必要はありません。

GNU 版の `strip` が必要なので、macOS ユーザーの方は以下のコマンドでインストールしてください。

```sh
$ brew install binutils
```

#### target

デフォルトで `"x86_64-unknown-linux-musl"` が入っていると思いますが、そのままでOKです。

#### [dependencies]

ここに書いたクレートが、プロジェクト作成時に `Cargo.toml` へとコピーされます。
とりあえず新ジャッジサーバーで使えるクレートを全部指定しておきたい場合は、以下をコピペしてください。
（以下のクレートは、バイナリ提出せずとも使えるクレートです）

__※依存クレートが多くなるとビルド時間がとても長くなるので、実際には本当に使うクレートだけに絞って書いたほうが良いと思います。__

```toml
[dependencies]
num = "0.2.1"
num-bigint = "0.2.6"
num-complex = "0.2.4"
num-integer = "0.1.42"
num-iter = "0.1.40"
num-rational = "0.2.4"
num-traits = "0.2.11"
num-derive = "0.3.0"
ndarray = "0.13.0"
nalgebra = "0.20.0"
alga = "0.9.3"
libm = "0.2.1"
rand = { version = "0.7.3", features = ["small_rng"] }
getrandom = "0.1.14"
rand_chacha = "0.2.2"
rand_core = "0.5.1"
rand_hc = "0.2.0"
rand_pcg = "0.2.1"
rand_distr = "0.2.2"
petgraph = "0.5.0"
indexmap = "1.3.2"
regex = "1.3.6"
lazy_static = "1.4.0"
ordered-float = "1.0.2"
ascii = "1.0.0"
permutohedron = "0.2.4"
superslice = "1.0.0"
itertools = "0.9.0"
itertools-num = "0.1.3"
maplit = "1.0.2"
either = "1.5.3"
im-rc = "14.3.0"
fixedbitset = "0.2.0"
bitset-fixed = "0.1.0"
proconio = { version = "0.3.6", features = ["derive"] }
text_io = "0.1.8"
whiteread = "0.5.0"
rustc-hash = "1.1.0"
smallvec = "1.2.0"
```

#### template

ここに書いた内容が、問題ごとのコードファイルの初期コードとして展開されます。
例えば、以下のように設定ファイルに書いておくと

```toml
template = """
use proconio::{input, fastout};

#[fastout]
fn main() {
    todo!();
}
"""
```

プロジェクト作成したときに、以下のように展開されます。

```rust:A~Fの6問構成であれば、a.rsからf.rsまで6つのファイルが作成されます
use proconio::{input, fastout};

#[fastout]
fn main() {
    todo!();
}
```

# 使い方

## プロジェクトを作成する

[AtCoder Beginner Contest 163](https://atcoder.jp/contests/abc163) のためのプロジェクトを作成することを考えます。

以下のコマンドを実行します。

```sh
$ cargo atcoder new abc163
```

`abc163` というディレクトリができます。ディレクトリ構造は以下のような感じです。

```sh
abc163
├── Cargo.lock
├── Cargo.toml
└── src
    └── bin
        ├── a.rs
        ├── b.rs
        ├── c.rs
        ├── d.rs
        ├── e.rs
        └── f.rs
```

`Cargo.toml` の `dependencies` にさきほど設定ファイルで書いたものが、そして `src/bin/` 以下にある `a.rs` などにも設定ファイルの `templates` に書いた内容があることが確認できると思います。

ちなみに、プロジェクト作成時に指定した `abc163` というのは、コンテストページのURLの末尾に書いてあるものです。
例えば [パナソニックプログラミングコンテスト2020](https://atcoder.jp/contests/panasonic2020) のプロジェクトを作成したいときは、このコンテストのURLが
`https://atcoder.jp/contests/panasonic2020`
なので、以下のようにすればOKです。

```sh
$ cargo atcoder new panasonic2020
```

## テストケースを試す

[ABC163 の A問題](https://atcoder.jp/contests/abc163/tasks/abc163_a)を解いてみました。

```rust:a.rs
use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        r: f64,
    }
    println!("{}", 2.0 * r * std::f64::consts::PI);
}
```

テストケースが通るか確認してみます。以下のコマンドを実行します。

```sh
$ cargo atcoder test a
```

以下のように実行され、テストが通ることが確認できました。テストケースをブラウザからコピペして1つ1つ試す必要はもうありません。

![Apr-27-2020 22-15-04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151210/058ba3e4-cab0-c3de-b05e-af9980e25821.gif)

## 提出する

テストが通ったので、提出しましょう。A問題を提出する場合は以下のコマンドを実行します。

```sh
$ cargo atcoder submit a
```

このようになります。🎉 AC 🎉

<img width="617" alt="スクリーンショット 2020-04-27 22.36.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151210/d383a293-547c-3169-5f72-55f857e30f84.png">


なお、 `submit` する時にもテストケースが通るかどうかの確認がされます。なので `test` せずにいきなり `submit` でも良いと思います。

ちなみに、`submit` コマンドは、デフォルトでは提出前のテストケースチェックで通過しなかった場合には提出を行わない仕様となっています。
「いくつか答えがありうるけど、そのうち1つが出力されてればOK」というタイプの問題については、用意されているテストの出力例と一致しなくても正解である、という場合がありえます。
この問題タイプの場合には `submit` に `--force` オプションをつけてください。テストが通らなくても提出を行うようになります。
（`-f` でもOKです）

```sh:テストで落ちてもsubmitしたいとき
$ cargo atcoder submit a --force
```

# 最後に

**cargo-atcoder** の導入方法と主要機能の使い方をご紹介しました。

超強力な機能であるバイナリ提出に関して「闇の力」という表現をしましたが、運営サイドとしてもこれはグレーであるという認識のようで、chokudaiさんが以下のようなツイートをしています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">バイナリ提出については、アップデート頻度が落ち着き次第、何かしらの制限をかける可能性が結構あります。そのあたりはご了承くださいな。</p>&mdash; chokudai(高橋 直大)🌸🍆🍡 (@chokudai) <a href="https://twitter.com/chokudai/status/1223162857056980992?ref_src=twsrc%5Etfw">January 31, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

どのような制限になるかは分かりませんが、さきほど書いた通りバイナリ提出はファイルサイズが大きくなるという欠点があります。例えば提出ファイルサイズの上限が現在の 512KiB から 64KB に下げられるだけでも、バイナリ提出は厳しくなるものと思われます。
（Codeforces のファイズサイズ上限が 64KB です）

バイナリ提出は **cargo-atcoder** の超強力な機能の1つであることは間違いないですが、それを抜きにしても、コマンドラインからのテストケースの実行や提出などはとても体験が良いものなので、ぜひお試しください。


別の **cargo-xxx** として、@hatoo@github さん作の **cargo-snippet** があります。スニペット管理をスマートに行えるこれまた素晴らしいツールです。
こちらもおすすめです！
[Rustで競技プログラミングをするときの"スニペット管理"をまじめに考える(cargo-snippetの紹介) - Qiita](https://qiita.com/hatoo@github/items/5c6814e72ddd2ecaf48f)