最近RustでAtCoderを始めました。始めるにあたってVSCodeでのRustの開発環境の構築をしたので、そのメモを書いていきます。
環境
- macOS Big Sur 11.5.2
- 2021年9月21日時点での情報です。
Rust/VSCodeの開発環境の構築
まずは、AtCoder以外のプロジェクトにも必要な部分の開発環境の構築をします。
rustupのインストール
Rust をインストール - Rustプログラミング言語 に従ってrustupをダウンロードし、Rustの諸ツールをインストールします。
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
途中、次のように聞かれるので1
を選択します。
1) Proceed with installation (default) 2) Customize installation 3) Cancel installation >
これで、rustup
と以下のRustのツールがインストールされます。
- cargo (パッケージマネージャー)
- clippy (Linter)
- rust-docs (ドキュメンテーション)
- rust-std (標準ライブラリ)
- rustc (コンパイラ)
- rustfmt (フォーマッタ)
次のように言われるので、ターミナルを再起動します。
To get started you may need to restart your current shell. This would reload your PATH environment variable to include Cargo's bin directory ($HOME/.cargo/bin).
インストールできているかを確認します。
$ rustc -V rustc 1.55.0 (c8dfcfe04 2021-09-06) $ cargo -V cargo 1.55.0 (32da73ab1 2021-08-23) $ rustup -V rustup 1.24.3 (ce5817a94 2021-05-31) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: The currently active `rustc` version is `rustc 1.55.0 (c8dfcfe04 2021-09-06)`l
rust-analyzer(VSCode拡張機能)のインストール
VSCodeでRustを使いやすくするために、rust-analyzer(VSCode拡張機能)をインストールします。
rust-analyzer(VSCode拡張機能)には、主に以下の機能があります(リンク先より引用)。
- code completion, imports insertion
- go to definition, implementation, type definition
- find all references, workspace symbol search, rename
- types and documentation on hover
- inlay hints
- semantic syntax highlighting
似た拡張機能として、Rust(VSCode拡張機能)があるのですが、rust-analyzer(VSCode拡張機能)のほうが使いやすかったので、rust-analyzer(VSCode拡張機能)を使用しています。
例えば、次のようなコードを書く場合には、Rust(VSCode拡張機能)ではmap
やsum
などの補完が効かなかったのですが、rust-analyzer(VSCode拡張機能)ではこれらの補完が効きます。
(0..3).map(|x| 2*x).sum::<i32>();
VSCodeによるコンパイル・実行
VSCodeによるコンパイル・実行をしてみたいと思います。cargo new
を用いて試しに適当なプロジェクトを作成し、VSCodeで開きます。
$ cargo new test_project $ code test_project
VSCodeでsrc/main.rs
を開きます。そこにはすでにHello Worldのコードが用意されています。
main
関数の上の部分にRun|Debug
があり、このボタンを押すとリリースモードでの実行やソースコードでの実行ができます。
右下に次のようなエラーが出た場合は、CodeLLDB(VSCode拡張機能)をインストールします。
Run
やDebug
を押して実行すると、ターミナルのタブにHello, world!
と出力されます。
なお、VSCodeではなくコマンドで実行する場合は、以下のように実行できます。
$ cargo run Compiling test_project v0.1.0 (/Users/test/rust_test/test_project) Finished dev [unoptimized + debuginfo] target(s) in 0.46s Running `target/debug/test_project` Hello, world!
Clippy(Linter)の設定
次にRustのLinterであるClippyの設定をします。ClippyははじめのRustのインストールですでにインストールされています。 rust-analyzer(VSCode拡張機能)でClippyが使えるように設定をします。
VSCodeの設定を開き、拡張機能のRust Analyzerを選びます。Check On Save: Command
という項目があり、デフォルトはcheck
になっているので、これをclippy
に変更します。
setting.json
で設定する場合には、setting.json
に次を追加します。
"rust-analyzer.checkOnSave.command": "clippy"
次にClippyの動作確認をします。
試しに先程のmain.rs
に次のようなコードを書いてみます。
fn main() { let _x = 3.14159; println!("Hello World"); }
そうすると、3.14159
に赤い波線が表示され、マウスオーバーすると次のような警告が表示されます。
このように良くないコードに対して警告や注意を出してくれるのがClippyの機能です。
参考: How to use Clippy in VS Code with rust-analyzer? - help - The Rust Programming Language Forum
AtCoder特有の開発環境の構築
これまでに、AtCoderに関係ない部分の開発環境の構築の話をしてきました。ここからはAtCoder特有の話をしていきます。
AtCoderで使われているRustのバージョン・クレート
まずは、AtCoderで使われているRustのバージョン、AtCoderで使えるクレートの確認をします。
AtCoderの適当なコンテストのルール(例えば、ルール - AtCoder Beginner Contest 220)を確認するとRustのバージョンが1.42.0
であることが確認できます。
また、このページの下の方に以下のような記述があります。
このページのリンク先に飛ぶと、使用可能なライブラリ(Rustではクレートと呼ぶ)が確認できます。
これらの使用可能なクレートについては 2020 Update · rust-lang-ja/atcoder-rust-resources Wiki · GitHub のページで詳しく解説されています(このページはものすごい便利です)。
Rustのバージョンの固定化
AtCoderで使われているRustのバージョンは1.42.0
です。一方、自分の環境のRustが1.42.0
とは限りません。
$ rustc -V rustc 1.55.0 (c8dfcfe04 2021-09-06) $ cargo -V cargo 1.55.0 (32da73ab1 2021-08-23)
バージョンが違うと、自分の環境では大丈夫だが、AtCoderの環境ではコンパイルエラーになるということがあります。(例えばbool::thenという便利な関数があるのですが、これは1.50.0
からの機能なので、AtCoderでは使用できません。)
そこで、特定のプロジェクトを使う場合にのみRustのバージョンが1.42.0
になるように設定します。
バージョンを1.42.0
にしたいプロジェクトにrust-toolchain
というファイルを作成し、次の1行をrust-toolchain
に書きます。
1.42.0
その後、Rustの適当なコマンドを実行すると、1.42.0
のRustがインストールされます。
$ cargo -V info: syncing channel updates for '1.42.0-x86_64-apple-darwin' info: latest update on 2020-03-12, rust version 1.42.0 (b8cedc004 2020-03-09) info: downloading component 'cargo' info: downloading component 'clippy' info: downloading component 'rust-docs' info: downloading component 'rust-std'cd info: downloading component 'rustc' info: downloading component 'rustfmt' info: installing component 'cargo' info: installing component 'clippy' info: installing component 'rust-docs' info: installing component 'rust-std' info: installing component 'rustc' info: installing component 'rustfmt' cargo 1.42.0 (86334295e 2020-01-31)
バージョンを確認してみると、確かに1.42.0
に変更されています。
$ cargo -V cargo 1.42.0 (86334295e 2020-01-31)
AtCoderで使えるクレートをCargo.tomlに指定
AtCoderで使えるクレートを自分の環境でも使えるようにします。
AtCoder用のCargo.toml
の雛形がatcoder-rust-base/Cargo.toml at ja · rust-lang-ja/atcoder-rust-base · GitHubに記載されています。
AtCoderで使用可能なクレートについては上記の雛形の[dependencies]
以下に記述されています。
このCargo.toml
の雛形(特に[dependencies]
以下)をcargo init
で生成されたCargo.toml
に貼り付け、使いたいクレートのコメントアウトを外すことで、クレートを使用することができます。
参考: Cargoパッケージの作成 (TODO) - AtCoderコンテストにRustで参加するためのガイドブック (cargo-generateは使用していませんが、もしかしたら便利かもしれません)
これで、AtCoderをRustで解くのに必要な環境は構築できました。以降、あると便利な設定やツールなどを紹介します。
複数のエントリーポイントの設定
cargo new
で作成したプロジェクトの場合、エントリーポイントはsrc/main.rs
のmain
関数のみです。AtCoderでは問題ごとにエントリーポイント(main
関数)を作りたくなります(1つの問題に1つのプロジェクトという運用もあるかもですが)。
ここではCargo.toml
を編集してエントリーポイントを増やします。
例えば、ABC219のAとB用のソースコードをそれぞれsrc/abc219/maina.rs
, src/abc219/mainb.rs
に用意したとします。このままでは、Run|Debug
のボタンがなく、実行ができません。
実行できるようにするために、Cargo.toml
に以下の記述を追加します。
[[bin]] name = "abc219_a" path = "src/abc219/maina.rs" [[bin]] name = "abc219_b" path = "src/abc219/mainb.rs"
すると、Run|Debug
のボタンが現れて、実行できるようになります。
VSCodeではなくコマンドで実行する場合は、Cargo.toml
のname
の値を指定すると実行できます。
$ cargo run --bin abc219_a Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running `target/debug/abc219_a` Hello, world! - ABC219-A
スニペット管理ツール: cargo-snippet
競技プログラマー向けのRustのスニペット管理ツールとして、cargo-snippetがあります。以下のようにインストールできます。(Rustのバージョンが1.42.0
の環境でインストールしようとすると失敗します。その場合はrust-toolchain
のあるプロジェクトの外で実行すれば大丈夫です。)
# rustfmtがインストールされていない場合はrustfmtをインストール # 普通はRustのインストール時に一緒にインストールされている $ rustup component add rustfmt $ cargo install cargo-snippet --features="binaries"
cargo-snippetの使い方については、cargo-snippetの作者による以下の解説記事に詳しく書いてあります。
ここでもcargo-snippetの使い方について軽く紹介します。
まずは、インストールしたcargo-snippetのバージョンを確認します。
$ cargo snippet -V cargo-snippet 0.6.5
次にCargo.toml
の[dependencies]
以下に次の記述を追加します。バージョンは上記で確認したバージョンが良いかと思います。
cargo-snippet = "0.6.5"
次にスニペット用のコードを書きます。例えばリンク先でも記述されているgcd
をmain.rs
に書いてみます。
use cargo_snippet::snippet; #[snippet] fn gcd(a: u64, b: u64) -> u64 { if b == 0 { a } else { gcd(b, a % b) } }
スニペットが書けたら、cargo snippet -t vscode
というコマンドを実行します(ここではVSCodeでの開発環境構築をしているのでVSCode用のスニペットを出力させていますが、Vim用のスニペットを出力させることもできます。)
$ cargo snippet -t vscode { "gcd": { "prefix": "gcd", "body": [ "fn gcd(a: u64, b: u64) -> u64 {", " if b == 0 {", " a", " } else {", " gcd(b, a % b)", " }", "}" ] } }
出力されたコードを.vscode/cargo_snippet.code-snippets
に保存します。(拡張子が合っていればファイル名は何でもいいです)
すると、gcd
というスニペットが使えるようになり、選択すると先程書いた関数gcd
のコードが展開されます。
ちなみに、自分のスニペットは atcoder_rust/src/mylib at main · paruma/atcoder_rust · GitHub に置いてあります。(ModIntやUnionFindなどを置いてます。)
個人的なスニペットの置き場
自分はスニペットのコードをsrc/mylib
というフォルダの中に置いています(参考: atcoder_rust/src/mylib at main · paruma/atcoder_rust · GitHub )。
ただし、そのまま置いてもrust-analyzer(VSCode拡張機能)がこのプロジェクトのソースコードと認識してくれず、あまり警告を表示してくれなかったり、型ヒントが表示されなかったりします。そこで、src/mylib
に置いたソースコードをこのプロジェクトのソースコードであると認識させる必要があります。
まずはsrc/mylib
というフォルダを認識させます。まずはsrc/lib.rs
というファイルを作成し、次のように記述します。
pub mod mylib;
次にsrc/mylib
の中のファイルを認識させます。例えばsrc/mylib/gcd.rs
を認識させる場合、src/mylib.rs
を作成し、次のように記述します。
pub mod gcd;
すると、src/mylib/gcd.rs
が認識され、ちゃんと警告などが表示されるようになります。
この設定により、テストコードの実行もできるようになります。
個人的なcargo-snippetの使い方
cargo snippet -t vscode
で出力されるスニペットについて少し言及しておきます。
{ "gcd": { "prefix": "gcd", "body": [ "fn gcd(a: u64, baa: u64) -> u64 {", " if b == 0 {", " a", " } else {", " gcd(b, a % b)", " }", "}" ] } }
このスニペットはRustファイル以外のファイル(例えばCargo.toml
)を書いているときであっても、サジェストの候補として表示されてしまいます。
これを防ぐためには、出力されたスニペットにRustファイル用のスニペットであることを追加で指定する必要があります。具体的には、次のように"scope": "rust",
を追加する必要があります。
{ "gcd": { "scope": "rust", // 追加した行 "prefix": "gcd", "body": [ "fn gcd(a: u64, baa: u64) -> u64 {", " if b == 0 {", " a", " } else {", " gcd(b, a % b)", " }", "}" ] } }
これは、sed
を使いcargo snippet
の出力を置換して出力させれば解決できます。
$ cargo snippet -t vscode | sed -r "s/\"prefix\"/\"scope\": \"rust\",\n \"prefix\"/" { "gcd": { "scope": "rust", "prefix": "gcd", "body": [ "pub fn gcd(a: u64, b: u64) -> u64 {", " if b == 0 {", " a", " } else {", " gcd(b, a % b)", " }", "}" ] } }
また、スニペットを更新した際に、いちいち.vscode/cargo_snippet.code-snippets
に出力されたスニペットを追加するのは面倒です。そこで、次のようなシェルスクリプトsnippet.sh
を作成し、このシェルスクリプトを実行することで自動で.vscode/cargo_snippet.code-snippets
が更新されるようにしています。
#!/bin/sh cargo snippet -t vscode | sed -r "s/\"prefix\"/\"scope\": \"rust\",\n \"prefix\"/" > .vscode/cargo_snippet.code-snippets
個人的なプロジェクトのディレクトリ構造
AtCoder用のプロジェクトを作成する際には、以下の3パターンが考えられます。
- 1つの問題に対して1つのプロジェクトを作る。
- 1つのコンテストに対して1つのプロジェクトを作る。
- 1つのAtCoder全体のプロジェクトを作り、すべてのコンテストでこのプロジェクトを使用する。
ここらへんは好みの問題だと思いますが、自分は3つ目を採用しています。(他のコンテストのソースコードを素早く参照したいのと、複数プロジェクトあると管理が大変そうなため。)
AtCoder用のプロジェクトのディレクトリ構造は以下のようにしています。(いくつか省略しています)
. ├── .vscode: VSCodeプロジェクトの設定フォルダ | ├── cargo_snippet.code-snippets: cargo-snippetが出力するスニペットの置き場 | └── rust.code-snippets: その他の手動で書いたスニペットの置き場 ├── src │ ├── contest: コンテスト用のディレクトリ │ │ ├── abc207 │ │ │ ├── mainb.rs │ │ │ └── mainc.rs │ │ ├── abc208 │ │ │ ├── mainb.rs │ │ │ ├── mainc.rs │ │ │ └── maind.rs │ │ └── template.rs: ソースコードのテンプレート │ ├── mylib: スニペットのソースコードの置き場 │ │ ├── bin_search0.rs │ │ ├── modint_field.rs │ │ └── union_find0.rs │ ├── sandbox: なにか適当なソースコードを書きたくなったときに書く場所 │ │ └── sandbox1.rs │ ├── lib.rs │ ├── mylib.rs │ └── sandbox.rs ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain ├── snippet.sh: 上記で解説したスニペット生成用スクリプト ├── web_task.sh: 問題ページを開くシェルスクリプト(後述) └── contest.py: コンテスト用のコードなどを用意するスクリプト(後述)
このプロジェクトは以下のGitHubに置いてあります。詳しくはそちらを参照してください。
https://github.com/paruma/atcoder_rustgithub.com
その他用意したツール
コンテスト名と問題を指定したら問題のページが開かれるツール
次のようなシェルスクリプトweb_task.sh
を作成しました。
#!/bin/sh if [ $# -ne 2 ]; then echo "usage: ./web_task.sh [contest_name] [task_name]" 1>&2 exit 1 fi open "https://atcoder.jp/contests/$1/tasks/$1_$2"
例えば、./web_task.sh abc219 b
を実行すると、https://atcoder.jp/contests/abc219/tasks/abc219_b が開かれます。
(Alfred(Macのランチャーアプリ)で同様の機能を実装すると便利そうだなぁって思っているが作ってはいない。)
ソースファイルの生成ツール
新しい問題を解くときにいちいちファイルを作成してテンプレートをコピーしてCargo.toml
に設定追加してってやるのは面倒なので、それらのことをやってくれるスクリプトcontest.py
(ソースコード)を作りました。
例えば./contest.py abc206 b c
を実行すると、src/contest/abc206/mainb.rs
とsrc/contest/abc206/mainc.rs
が生成されます。ファイルの中身はあらかじめ用意したテンプレートファイルsrc/contest/template.rs
をコピーしたものです。さらに、Cargo.toml
用の記述が以下のように出力されます。
$ ./contest.py abc206 b c [[bin]] name = "abc206_b" path = "src/contest/abc206/mainb.rs" [[bin]] name = "abc206_c" path = "src/contest/abc206/mainc.rs"
これをCargo.toml
に貼り付けてソースコードが実行できるようにしています。
使ってはいないが便利そうなツール
コンテスト用のソースファイルの生成からテストケースでのテスト、コードの提出までコマンドでできるツールとして、cargo-atcoderやcargo-competeがあります。自分は使用していませんが、人によっては便利だと思うので軽く紹介しておきます。
最後に
自分が行った、AtCoder用のVSCodeでのRustの開発環境の構築について紹介しました。
ぜひ皆さんもRustでAtCoderをやってみましょう!