記事で載せてる実装例とかほぼ全部Rustのコードなことからわかるように、私はRust使いです。(私の記事読んだことある人なんでほぼいないでしょうが)
たまに、Rustで競プロやりたいけど環境構築の方法がよくわからん、みたいな人もいるので、お手軽環境構築していきましょう。
リッチな環境じゃないですが、私はこの環境で出ててあんまり不便感じてないので、まあ大丈夫なはずです。
WSLをインストールする
のっけからこれかよって感じですが、WSLを入れていきましょう。
別にWindowsでも当然Rustは使えるんですが、私はWindowsでのプログラミングのことをよく知らないので、WSL入れていきます。
当たり前ですが、生のLinux使ってるならこの手順は不要です。
手順は簡単で、管理者としてPowershellを立ち上げてwsl.exe --install
すればよいです。
必要に応じて以下を参照しましょう。ここに書いてある通り、よっぽど古い環境使ってるとかでなければwsl.exe --install
で入るはずです。
WSL のインストール | Microsoft Learn
インストールされるのはUbuntuなので、Ubuntuが嫌いな人は別のディストロ入れればいいと思います。
wsl.exe --list --online
すればわかりますが、Debian, Oracle Linux, Kali Linux, Open SUSE, SLESとか使えます。
build-essentialを入れる
sudo apt install build-essential
とすれば入ります。ほかのディストロにも似たようなのがあると思います。
なんでこんなの入れるの?って話ですが、後で導入するRust Analyzerがerror: linker 'cc' not found
などというエラーで死ぬことがあるからです。
gcc入れるだけで十分な気もするのですが、なんかトラブっても嫌なのでとりあえず入れときます。
rustupする
LinuxでのRust環境の導入は超簡単で、rustupというツールを実行すれば勝手に入ります。
たぶんcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
とかいうコマンドが書いてあると思うので、これをそのまま黒窓にコピペして実行します。
今はこれが書いてありますが、将来もそうかはわからないので、ちゃんと公式ページからコマンドを拾ってきてください。
curl
がないよって怒られたら、sudo apt update
して、sudo apt install curl
でインストールしてから実行しましょう。
実行すると1回だけ入力を求められると思いますが、これはそのままEnterを叩けばdefaultでインストールを進められます。
インストール後、シェルを再起動するか、あるいは出力の末尾に出てくるsource "$HOME/.cargo/env"
をそのままコピペして実行すれば、パスが通ります。
これで、cargo
, rustc
, rustfmt
, rustup
あたりのツールが入っているはずなので、確認してみましょう。command -v
かwhich
で、それぞれのツールのインストール場所のパスが見えればOKです。
私は今dockerでテキトーに立てたコンテナで試しているので、/root
配下に置かれています。わざわざsudo
でrustupしたりしてなければ、ホームディレクトリ配下にインストールされることになるでしょう。
$ command -v cargo /root/.cargo/bin/cargo $ command -v rustc /root/.cargo/bin/rustc $ command -v rustup /root/.cargo/bin/rustup $ command -v rustfmt /root/.cargo/bin/rustfmt
手間でなければ一応バージョンも見ておきます。
$ cargo --version cargo 1.75.0 (1d8b05cdd 2023-11-20) $ rustc --version rustc 1.75.0 (82e1608df 2023-12-21) $ rustup --version rustup 1.26.0 (5af9b9484 2023-04-05) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: The currently active `rustc` version is `rustc 1.75.0 (82e1608df 2023-12-21)` $ rustfmt --version rustfmt 1.7.0-stable (82e1608 2023-12-21)
補完の設定をする
オプションです。おそらく現時点ではcargo
を入力した後、タブ補完とか試しても何も反応しないでしょう。
これでは不便なので、シェルの補完をやっていきます。
もしbash-completion
がインストールされていない場合は、sudo apt install bash-completion
などでインストールしておきましょう。
たしかWSLでは必要なかったと思いますが、念のためです。
rustup completions
コマンドを叩くとめっちゃ長いヘルプが出てきますが、その中にシェルごとの補完の設定の仕方が書いてあります。
WSLを何もカスタムしていなければbashを使っているでしょうから、BASH:
と書いてあるところを読みながらコマンドを打ち込みましょう。
今時点では、以下のようなコマンドを打ち込めと書いてあります。
$ mkdir -p ~/.local/share/bash-completion/completions $ rustup completions bash >> ~/.local/share/bash-completion/completions/rustup $ rustup completions bash cargo >> ~/.local/share/bash-completion/completions/cargo
このあと1回ログアウトせよ、と書いてあるので、シェルを立ち上げ直します。
ちなみにdockerで試しているとき、ログアウトせずに補完を試したらbash: _get_comp_words_by_ref: command not found
なるエラーが出ていました。
これはsource /etc/bash_completion
で治ります。
kubectl bash completion doesn't work in ubuntu docker container - Stack Overflow
シェルを立ち上げ直したら、補完を試しましょう。rustup
コマンドも同様に補完が効くようになっているはずなので、手間でなければ試してください。
$ cargo <TAB> ~出力例は省略。上手くいっていればadd, check, clean, buildなど多数のコマンドが並んでいるはず~
エディタを入れる
なんでもいいです。VimでもEmacsでも好きなものを使うと良いでしょう。
私はVisual Studio Codeしか知らないので、その前提で話を進めます。
Visual Studio Code - Code Editing. Redefined
Visual Studio Codeをインストールすると、WSLからcode
コマンドが使えるようになるはずです。
Windowsから起動するのではなくWSLのシェルからcode <target directory>
で起動することで、勝手にVisual Studio CodeがWSLに接続するようになります。
拡張機能を入れる
もちろん好きなものを自己責任で入れればよいですが、とりあえずRust Analyzerというやつは入れましょう。
一応リンクは貼りましたが、Visual Studio Codeの左端にある、右上の欠けた田のマークからインストールするのが楽です。
rust-analyzer - Visual Studio Marketplace
私は毎回環境立てるときは以下のRust Extension Packを入れてますが、特にどっかが公式で出してるわけではないので、入れる場合は自己責任で入れてください。
Rust Analyzerもここに入ってます。
Rust Extension Pack - Visual Studio Marketplace
コンテスト用のクレートを作る
Rustではクレートという単位でプロジェクトを分けます。依存モジュールもクレート単位で解決されます。
大きくバイナリクレートとライブラリクレートがありますが、混在できます。また、1つのクレートに複数のバイナリを持たせることもできます。
競プロやるにあたっては、例えば1つだけバイナリクレートを作って毎回上書きして書き捨てるとか、問題1つにつき1クレートとか(さすがにいないかな…)、色々やり方はあると思います。
私は1クレートで1コンテストにしているので、その方法でやります。
まずcargo new
で新規クレートを作りましょう。例えば、
$ cargo new abc300
で、abc300
というディレクトリが作られます。あるいは、mkdir abc300; cd abc300; cargo init
でも同じことになります。
作成されたabc300
は、以下のような構造になっているはずです。(もしかしたら.gitignoreがあるかもしれませんが、些細な差です)
$ tree abc300/ abc300/ |-- Cargo.toml `-- src `-- main.rs 1 directory, 2 files
コンテスト用クレートが出来たら、これをcode abc300
で開いてみましょう。
開いたウィンドウで、ターミナル > 新しいターミナル としてターミナルを開いておきます。
そして、
$ mkdir src/bin -p $ cd src/bin $ touch {a..g}.rs $ code *
として、コンテストで使うソースファイルを作ります。
src/main.rs
はどうしたんだって話ですが、私は使いません。なので、別に消してもいいですし、残しておいても無害なのでそのままでも良いです。
外部クレートを入れる
RustでもAtCoderであれば外部ライブラリがたくさん使えるので、以下を参照しながら好きなものを入れていきましょう。
(スプレッドシートが真と書いてあるので、スプレッドシートを見たほうが良いかもしれない…)
https://img.atcoder.jp/file/language-update/language-list.html
外部ライブラリのダウンロードにはcargo add
コマンドを使うと良いです。
例えばRust競プロer御用達のproconio
をインストールするには、カレントディレクトリがabc300
配下であるのを確認して
$ cargo add proconio@=0.4.5 --features "derive"
とすることで依存クレートに加えることができます。
--features
は外部クレートのオプションの機能を指定するのに使います。proconio
には出力高速化のためにfastout
というマクロが用意されていますが、これは--features "derive"
を指定したときのみ使えます。
デフォルトではcrates.ioと呼ばれるレジストリからクレートはダウンロードされますが、ローカルフォルダやGithubのリポジトリからダウンロードすることも可能です。
詳しくはドキュメントを読んでみてください。
cargo add - The Cargo Book
ちなみに、cargo add
の操作は、さっきクレートのディレクトリ構造を見たときにチラッと出てきたCargo.toml
の[dependencies]
という項目にエントリを加えるのと同じです。
なので、手書きでCargo.toml
を書き換えることでも外部クレートの追加は可能です。
コードの実行
あとはコードを書いて実行するだけです。
例えば、src/bin/a.rs
を実行するときには、以下のようにすれば実行できます。
$ cargo run --bin a
また、cargo
にはreleaseビルドとdebugビルドがあります。デフォルトではdebugビルドで、--release
を指定することでreleaseビルドになります。
$ cargo run --bin a --release
だいたいdebugビルドでも困りませんが、AtCoderの環境はreleaseビルドであるということだけ覚えておいたほうが良いでしょう。
また、cargo new
で作成されるsrc/main.rs
を実行する場合には、クレート名を--bin
の引数にする必要があります。
$ cargo run --bin abc300
src/bin
配下のバイナリがない場合には、--bin
がなくてもsrc/main.rs
が実行されます。
$ cargo run
ここまででコンテストに出るには十分な環境が整いました!あとの章はオマケです。読まなくても支障はないです。
自作のライブラリを使う
C++とかだとライブラリを展開してバンドルするツールを使ってるのをよく見ます。Rustにもそういうツールはあります。
いくつか種類はあるのですが、私はcargo-equip
というツールを使っています。
crates.ioに公開されているので、cargo install cargo-equip
で入手することができます。
使い方は簡単で、cargo equip --bin a > run.rs
として対象のバイナリが依存するライブラリを上手いことバンドルしたコードを標準出力に吐き出させ、リダイレクトで別のファイルに流せば、提出できる形になります。
ただ、何もオプションを付けないと森羅万象がバンドルされて大変なことになります。私が使うときは以下のオプションを付けて、AtCoderで使えるクレートを除外したり、cargo check
をパスしたり、minifyするオプションを付けてコード量を減らしたりといったことをしてもらっています。
cargo equip --exclude-atcoder-crates --remove docs --remove comments --exclude-atcoder-202301-crates --minify libs --no-rustfmt --no-check
長すぎるわ!って話ですが、cargo-equip
は仕様上いっぺんビルドが走るので、ちょっとでも処理を減らさないとバンドルに許容できない時間がかかります。(Rustのビルドは遅いことで有名)
なので、.bashrc
にequip
というエイリアスで、上記のオプションを全部ひっくるめて登録しています。
$ alias equip alias equip='cargo equip --exclude-atcoder-crates --remove docs --remove comments --exclude-atcoder-202301-crates --minify libs --no-rustfmt --no-check'
これでequip --bin a > run.rs
のように、楽にオプションもりもりコマンドを実行できます。
コンテスト用クレートをworkspaceにまとめる
(追記)しばらくこれを使っていて、いろいろ不便なことが出てきたので、使うのをやめました。(たとえば、cargo check
やビルドの時間が長くなる、rust-analyzer
がworkspace配下の他所のクレートのエラーを大量に拾って固まる、マラソンのテスターが上手く動かない、などなど…)
バイナリをどこかのディレクトリにまとめて容量を減らしたいだけであれば、後述の.cargo/config.toml
を使う方法が良いです。
一応、元の文章は残しておきます。
cargoにはworkspaceという機能があって、いくつかのクレートを1つの仮想的なクレート(?)の配下にまとめておくことができます。
workspace配下のクレートはバイナリを1か所に吐き出させることができるので、これでバイナリの容量を削減できます。
別にそんなことしなくて良くない?って思ってしまいそうですが、Rustはバイナリがデカいので、300回分のコンテストクレートを別々に作ってそれぞれビルドとかすると、数十GBとか下手すると100GB近い容量を消費することもザラです。(えぇ…)
workspaceを作るには、コンテスト用クレートをまとめるディレクトリを作り、そこでcargo init
します。生成されたCargo.toml
を以下のような内容に書き換えれば完成です。
[workspace] members = [ "abc100", "abc101", ....(その他多数のクレート) ] exclude = [] resolver = "2" [workspace.dependencies] ac-library-rs = "=0.1.1" segtree = { git = "https://github.com/tayu0110/tayu-procon.git", package = "segtree" } ....(その他多数の依存クレート)
exclude
とかresolver
は明に指定しなくても動きます(確かresolverは指定しないとwarningが出た気がするが覚えていない…)。
workspace配下のクレートでは、今までのように依存クレートを宣言する以外に、以下のような宣言の仕方ができるようになります。
[dependencies] ac-library-rs.workspace = true segtree.workspace = true .....
従来通りに指定した場合は依存クレートのバイナリがそのクレートのtarget/
配下に配置され、workspaceのdependenciesを引き継ぐように指定した場合はworkspace内でバイナリが共有されます。このおかげで、各クレート配下に存在するバイナリの量を最低限にできます。
a
とかb
とか雑な名前つけてたけど、そんなバイナリをごったまぜにして一か所に吐かせて大丈夫なんか?っていう点ですが、cargo
は賢いので、これを適切に処理してくれます。ただ、cargo-equip
はこれを処理してくれないので、私は若干改造して使っています。
バイナリをまとめて容量を減らす
cargo
の動作を制御するconfig.toml
をいうのを作ることで、バイナリを吐く宛先のディレクトリを変えることができます。
Configuration - The Cargo Book
コンテスト用クレート配下に.cargo/config.toml
を作成し、build.target-dir
を書き込めばよいです。
私はコンテスト用クレートをまとめたatcoder
というディレクトリの直下にtarget
ディレクトリを作り、そこにバイナリをまとめて吐かせたいので、
[build] target-dir = "../target"
という設定ファイルを書き込んでいます。(Cargo.toml
からの相対パスなので、../target
となります)
コンテスト用クレート作成スクリプト
ここまでで思ってる人多いと思いますが、めんどいですね。というわけでShellscriptで一発でコンテストごとのクレートを作れるようにしていきましょう。
私は以下のようなShellscriptを使っています。
#!/bin/bash -l if [ -z "$1" ]; then echo "Usage: ./make_contest.sh [CONTEST_NAME]" exit fi DIR="$1" if ls "$DIR" >/dev/null 2>&1; then echo "$DIR already exists." exit fi cargo new "$DIR" cd "$DIR" || exit mkdir .vscode -p cat <<EOF >.vscode/settings.json { "rust-analyzer.check.command": "check" } EOF mkdir .cargo -p cat <<EOF >.cargo/config.toml [build] target-dir = "../target" EOF cargo add ac-library-rs@=0.1.1 cargo add num@=0.4.1 cargo add rand@=0.8.5 cargo add regex@=1.9.1 cargo add permutohedron@=0.2.4 cargo add superslice@=1.0.0 cargo add itertools@=0.11.0 cargo add proconio@=0.4.5 --features derive cargo add bitset --git https://github.com/tayu0110/tayu-procon.git cargo add complex --git https://github.com/tayu0110/tayu-procon.git cargo add convolution --git https://github.com/tayu0110/tayu-procon.git cargo add ds --git https://github.com/tayu0110/tayu-procon.git --features "full" cargo add fenwick-tree --git https://github.com/tayu0110/tayu-procon.git cargo add flow --git https://github.com/tayu0110/tayu-procon.git cargo add geometry --git https://github.com/tayu0110/tayu-procon.git cargo add graph --git https://github.com/tayu0110/tayu-procon.git cargo add math --git https://github.com/tayu0110/tayu-procon.git cargo add matrix --git https://github.com/tayu0110/tayu-procon.git cargo add mincost-flow --git https://github.com/tayu0110/tayu-procon.git cargo add montgomery-modint --git https://github.com/tayu0110/tayu-procon.git cargo add polynomial --git https://github.com/tayu0110/tayu-procon.git cargo add rational --git https://github.com/tayu0110/tayu-procon.git cargo add segtree --git https://github.com/tayu0110/tayu-procon.git cargo add static-modint --git https://github.com/tayu0110/tayu-procon.git cargo add string --git https://github.com/tayu0110/tayu-procon.git cargo add suffix-array --git https://github.com/tayu0110/tayu-procon.git cargo add unionfind --git https://github.com/tayu0110/tayu-procon.git cargo add utility --git https://github.com/tayu0110/tayu-procon.git cargo add wavelet-matrix --git https://github.com/tayu0110/tayu-procon.git mkdir -p src/bin for prefix in {a..g}; do SRC=src/bin/$prefix.rs { printf "use proconio::*;\n\n" printf "fn main() {\n" printf " \n" printf "}\n" } >>"$SRC" cargo equip --exclude-atcoder-crates --exclude-atcoder-202301-crates --minify libs --no-rustfmt --no-check --remove docs --remove comments --bin "$prefix" >/dev/null done cargo build cargo build --release code .
.vscode/settings.json
に打ち込んでいるのはRust Analyzerがリントを行うコマンドを指定するオプションです。
ライブラリや趣味プロではclippy
という強力でアグレッシブなリントツールを使っていますが、コンテスト中だと鬱陶しいので、コンテスト用クレート内ではマイルドなcargo check
を使います。
最後の方ではcargo build
とcargo equip
を回しています。1回目のビルドは外部クレートのビルドも走るので、めちゃくちゃ遅いです。
下手するとABCのA問題のコードを書き切るより時間がかかりますので、1回空回ししておくとストレスが減ります。