始めに

マシンによる環境の違いで環境構築ができないという問題を解決するため、RustでAtCoderに取り組むための環境をDocker上で構築しました。
できる限り便利にしたいと思い、いろいろツールを入れています。

なお、この記事は2023に言語アップデートに伴い複数回書き直しています。

できる環境

クリックまたは簡単なコマンドを打つことでビルド、テスト、提出を行う。
ファイルからの入力でデバッグを行う。

こちらにコードを載せています
https://github.com/uriuriboo/atcoder_rust
一人ではなかなか厳しいところがあるので機能の追加や問題点、言語アップデートなどので改善案や報告がありましたらissueやPRなどをください!

注意点

    • 改行コードの違いでシェルスクリプトが動かないことがあったので注意してください。

 

    二回目以降環境もDockerで開くとシェルスクリプトが不要ですが走ってしまうのでCtrl+Cで中断してください。

環境、ツール

windows10、11
Docker
WSL2
vscode
cargo-compete
cargo-member
python3

vscodeの拡張機能

ローカルで扱うもの
Docker
Dev Containers

仮想環境で扱うもの(だいたい環境構築時に入ってます)
Rust(rust-anayzerではない)
CodeLLDB(デバッガー)
rust-anayzer
Github copilot
taskrunner

手順1 Dockerのインストール

windowsならまずWSL2をインストールします。
window10ならpowershellで

wsl --install

と入力してください。

その後Docker公式サイトからインストーラをダウンロードして実行してください。
macならdockerをすぐにインストールできたと思います。

参考となりそうなもの
https://learn.microsoft.com/ja-jp/windows/wsl/install

手順2 コンテナの立ち上げ

vscodeを立ち上げて拡張機能のDockerをインストールしてください。次に左下にある緑のボタンを押してください。

image.png

すると上部にOpen Folder in Cointanerと出てきますのでクリックし、ダウンロードしたリポジトリを開いてください。私はフォルダ名をatcoder_rust(githubのリポジトリ名と同じ)に設定しました。後にテストをする際のパスの指定に使用しますので気をつけてください。そうすれば簡単なRust環境の完成です。
ここで必要なツールが一部インストールされます。
途中で数字の入力が求められるため、こだわりがなければ 2 を入力してください
もしエラーがでるようならシェルスクリプトで改行の形式が原因かもしれません。

.devcontainer/postCreateCommand.bashでエラーが出たら(i)、(ii)の方法を試してください

image.png
image.png

(ii)以下のコマンドを打つ

$ sudo chmod a+x .devcontainer/postCreateCommand.bash

最後に.devcontainer/postCreateCommand.bashをコマンドラインから実行してください。
実行方法

$ ./.devcontainer/postCreateCommand.bash

立ち上げ後はAtCoderの環境にするか、ログインするか聞かれるのでコマンドラインのメッセージを読んで適宜操作してください。

手順3 vscodeの設定

拡張機能

デフォルトではrust-analyzerをアンインストールしてくださいrust1.42では動かないためです。今回は開発がサポートが中断したRust(拡張機能)を作成します。
繰り返しになりますがrustのバージョンが1.65より大きいならアンインストールしなくても大丈夫です、

次にRustのファイルを作成して開くとvscodeの右下の画面にRust langages serverがコンポーネントをインストールするか聞かれるのでYesを押すとコンポーネントインストールされます。これで補完が使えるようになります。

ちなみにrust-analyzerを無理矢理動かしている方法もあるようです。
https://zenn.dev/yajamon/articles/be689814d242f8

注意点

二つ以上のプロジェクトを作ると動かなくなるようです
https://zenn.dev/fah_72946_engr/articles/cf53487d3cc5fc
解決方法としてcargo-memberを使うことが上げられます

デバッグ

.vscode/launch.jsonに以下のコードを記述します。

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug",
            "sourceLanguages": [
                "rust"
            ],
            //"preLaunchTask": "cargo build",
            "internalConsoleOptions": "openOnSessionStart",
            "program": "${workspaceRoot}/target/debug/abc252-${fileBasenameNoExtension}",
            "args": [],
            "stdio": [
               "${workspaceFolder}/input.txt"
            ],
        }
    ]
}

ホームディレクトリ直下にinput.txtが入力ファイルです。
そしてデバッグしたいファイルを開きF10キーを押せば開始できます。

コンテストごとにprogramの項目にあるabc-252の部分を現在参加しているコンテストに書き換えてください。ここは今回作った環境の要改善です。
例:
abcコンテスト108回 → abc-108
arcコンテスト10回 → arc-10

2023/6/3追記
テストを行う際に自動でlaunch.jsonを書き換えるように設定しました。
こちらはまだ動かしていないので検証をします。
abcで動くことを確認しました。

スニペット

スニペットの機能を使うことで簡単にコードを呼び出すことができます。スニペットは破片という意味を持ちます。

詳しくはこちらをご覧ください。
https://qiita.com/12345/items/97ba616d530b4f692c97
ファイル→ユーザ設定→スニペットを選択し、コマンド欄にrustと打ち込みrust.jsonを開いて、よく使うコードを書きましょう。
私はテンプレートとして以下のスニペットを登録しています。

{
    "template": {
        "prefix": "tmplt",
        "body": [
            "#[allow(unused_imports)]",
            "use proconio::{marker::*, *};",
            "#[allow(unused_imports)]",
            "use std::{cmp::*, collections::*, *};\n",
            "#[fastout]",
            "fn main(){",
            "\tlet mut ans:usize = 0;",
            "\tinput!{",
            "\t\tn:usize,",
            "\t\ta:[usize;n]",
            "\t}\n",
            "\tprintln!(\"{}\",ans);",
            "}"
        ],
        "description": "テンプレート"
    },
}

手順4 ツールのインストール

$ touch rust-toolchain
$ echo "1.70" > rust-toolchain
$ cargo -V
$ rustup install 1.70
$ rustup install 1.70.0-x86_64-unknown-linux-gnu

環境構築で自動化できなかった部分です。

競技プロ補助ツールのcargo-competeがテストなどを行うために使用するrust-toolchainをインストールします。
昔rust-toolchainで動かなかったときいろいろ試した結果このようにインストールすると解決しました。(私自身このあたりはあまりわかっておらず勉強中です)

ここではもしかしたらrustup install 1.70はいらないかもしれないです。

追記
こちらに質問が投稿されていました。
https://stackoverflow.com/questions/49368232/what-is-a-default-host-triple-in-rust

公式に書いてありました。
https://github.com/rust-lang-ja/atcoder-rust-resources/wiki/2020-Update
https://doc.rust-lang.org/stable/rustc/platform-support.html

ここでrust-analyzerを使うなら1.42を1.70にしてください。
rust-analyerが動かないときはvscodeを再起動したら動きました。

問題を解くときの使用方法

ビルドとテスト

ビルドとテストを行うスクリプトは以下のように書いています
arcやagcに出るときはcontest変数の値を変更してください

#!/bin/bash
contest="abc"
num=$1
aplha=$2
cd /workspaces/atcoder_rust/${contest}${num}/src/bin/
cargo compete s ${aplha}

使い方

$ ./bt.bash コンテストの番号 問題のアルファベット

使用方法の具体例
ABCコンテスト277 A問題の場合(contestの変数は変更していません)

$ ./bt.bash 277 a

クリックで行う場合
2023/5/31追加

image.png
image.png

TASKRUNNER->Workspace->build & test をクリックすると新たにターミナルが開きテスト結果が表示されます。

問題の提出

提出するためのスクリプトは以下のように書いています

#!/bin/bash
contest="abc"
num=$1
aplha=$2
cd /workspaces/atcder_rust/${contest}${num}/src/bin/
cargo compete s ${aplha}

使い方

$ ./sub.bash コンテストの番号 問題のアルファベット

具体例
ABCコンテスト208 D問題の場合(contestの変数は変更していません)

$ ./sub.bash 208 d

試験的にTASKRUNNER->submitから提出できる機能を追加しました。
エラーがありましたらお知らせください。

環境設定の説明

postCreateCommand.bash

    1. rust-toolchainをが存在しているときcargo-competeを実行するとうまく実行できないことがあったため削除しています。はっきりはしりませんがエディションが2018にしか対応していないかあたりの問題だったと思います。

 

    1. ビルドとテストを行うスクリプトと提出用スクリプトに権限を与えています。

 

    1. cargo-competeでAtCoder向けの設定をしています

 

    1. コメントアウトした部分はシェルで実行したときエラーが起きたためコメントアウトして手動で行いました。

 

    1. ログイン機能を使ったログインをしています。

 

    1. fishとそのプラグインのfisherをインストールしています。

 

    1. AtCoder用の初期設定をしています

 

    1. AtCoderにログインをしようとしておりユーザ名とパスワード入力を求めています

 

    Rust用のac-libraryをクローンしています。
#!/bin/bash

if [ -e rust-toolchain ];then
    rm rust-toolchain
fi
echo give permission to bashfiles
chmod u+x bt.bash,sub.bash,scripts/bt.bash,scripts/sub.bash,scripts/change_launch.bash

# install rust1.70.0
echo "1.70" > rust-toolchain
cargo -V

touch Cargo.toml

# install tools
cargo install cargo-compete
cargo install cargo-member
cargo compete i atcoder
echo clone ac-library-rs
git clone https://github.com/rust-lang-ja/ac-library-rs.git
cargo member include ac-library-rs
# cargo member include library

echo login to atcoder
cargo compete l atcoder


# rustup install 1.70
# rustup install 1.70.0-x86_64-unknown-linux-gnu

# The installation of fish is your choice.
# echo install fish and fisher
# sudo apt-add-repository ppa:fish-shell/release-3
# sudo apt-get update
# sudo apt-get install fish
# curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher

tasks.json

.vscode/tasks.jsonとシェルスクリプトとTASKRUNNERを使ってクリックからビルドとテスト、提出を行えるようにしています。scriptフォルダに使用されるファイルは置かれています。

Rustのファイルを開いた状態で使うことを想定しており、pythonにそのファイルの絶対パスを渡してそのpythonからシェルスクリプトを呼び出しています。
参考
https://magicode.io/shunnya0715/articles/9734ba4922894b329a756b6f3592e8ed

{
	"version": "2.0.0",
	"tasks": [
		// {
			
		// 	"type": "cargo",
		// 	"command": "build",
		// 	"problemMatcher": [
		// 		"$rustc"
		// 	],
		// 	"group": "build",
		// 	"label": "rust: cargo build"
		// },
		// {
		// 	"type": "shell",
		// 	"command":[ "cd ${workspaceFolder}/abc253/src/bin/ & pwd",
		// 		//"cargo build & cargo compete t ${fileBasenameNoExtension}",
				
		// 	],
			
		// 	"group": "build",
		// 	"label": "rust: cd cargo build"
		// },
		// {
		// 	"type": "shell",
		// 	"command": [
		// 		"pwd"
		// 	],
		// 	"group": "test",
		// 	"label": "pwd"
		// },
		{
			"label": "build & test",
			"type": "shell",
			"command": "python3",
			"args": [
				"${workspaceFolder}/scripts/test.py",
				"${file}"
			],
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"problemMatcher": [],
		},
		{
			"label": "submit",
			"type": "shell",
			"command": "python3",
			"args": [
				"${workspaceFolder}/scripts/sub.py",
				"${file}"
			],
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"problemMatcher": [],
		},
		{
			"label": "rust: cargo compete new",
			"type": "cargo",
			"command": "compete",
			"args": [
				"new",
				"${input:contest}"
			]
		},
		{
			"label": "rust: cargo member include",
			"type": "cargo",
			"command": "member",
			"args": [
				"include",
				"${input:contest}"
			]
		},
		{
			"label": "new contest",
			"dependsOrder": "sequence",
			"dependsOn": [
				"rust: cargo compete new",
				"rust: cargo member include",
			],
			"presentation": {
				"echo": false,
				"reveal": "silent",
				"focus": false,
				"panel": "shared",
				"showReuseMessage": true,
				"clear": true
			},
			"problemMatcher": []
		}
	],
	"inputs": [
		{
			"id": "contest",
			"description": "contestID",
			"type": "promptString"
		}
	]
}

```json:settings.json
{
    "[rust]": {
        "editor.formatOnSave": true,
        "editor.formatOnPaste": true,
        "editor.formatOnType": true,
        "editor.defaultFormatter": "rust-lang.rust-analyzer",
    },
    "rust-analyzer.check.command": "clippy"
}

Rust言語使用時に保存、ペースト、タイプ時にRustfmtを使用してフォーマットを行うようにしてます。
なぜか保存時にしかフォーマットされないみたいでした。解決方法を模索中です。
また、clippyというリンターを使用してコードを静的解析しています。

scripts/test.py

ファイルの絶対パスからabcかarcかagcかコンテストの回数、問題のアルファベットを取得してシェルスクリプトに渡しています。
テストした.rsファイル用にlauch.jsonを書き換える処理も行っています。

import os,sys
import subprocess
from urllib.request import urlopen

def get_contest_id(path) -> tuple:
    parsed_path = path.split("/")
    contest_name = parsed_path[3][:3]
    contest_id = parsed_path[3][3:]
    problem_id = parsed_path[6][0]
    return contest_name,contest_id,problem_id

def download_test(contest_name,contest_id,problem_id):
    url = "https://atcoder.jp/contests/{0}/tasks/{0}_{1}".format(contest_id,problem_id)
    # cp = subprocess.run("pwd") /workspaces/atcoder_rust
    # cp = subprocess.run(["./bt.bash",contest_name,contest_id,problem_id])
    cp = subprocess.run(["scripts/change_launch.bash",contest_name,contest_id,problem_id])
    cp = subprocess.run(["scripts/bt.bash",contest_name,contest_id,problem_id])

contest_name,contest_id,problem_id = get_contest_id(sys.argv[1])
download_test(contest_name,contest_id,problem_id)

script/bt.bash

リポジトリ直下のbt.bashと似ていますがabcか、などを引数として受け取るように変更しています。

#!/bin/bash
contest=$1
num=$2
aplha=$3
cd /workspaces/atcoder_rust/${contest}${num}/src/bin/
cargo build & cargo compete t ${aplha}

devcontainer.json においてコンテナを作ったあと.devcontainer内のpostCreateCommand.bashを実行しています

注意点まとめ

コマンドラインからスクリプトを使うときは
launch.json
bt.bash
sub.bash
の三つのファイルをコンテスト、コンテストの番号つまりabc、arc、agcやその回数ごとに書き換える必要があること

postCreateCommand.bashはすでにコンテナが作成されているなら処理がいらないので中断すること

postCreateCommand.bashは改行コード、権限の関係で実行できないことがあるため、適宜直すこと

フォルダ名をatcoder_rustから書き換えないこと。書き換える場合はscript以下のパスを書き変える。

あったら便利そうなツール

github copilot

自動補完ツールその1
これはお金を払わないと使えないため課金したくない人は以下二つを推奨

Tabnine

機械学習を用いた自動補完ツールその2
重いですがかなり便利です
proにすると軽くなるらしい

Kite

機械学習を用いた自動補完ツールその3
enginをインストールしてそちらのプログラムからvscode-pluginをインストールすると使えるようになるらしい

今後の改善点、追加したい機能

    1. 環境構築を完全に自動化する

 

    1. グラフの可視化

 

    1. サンプルの自動生成

 

    1. cargo equipなどで外部のライブラリを使用しやすくする

 

    シェルスクリプトでエラーが起こったらユーザーに問題を通知

最後に

今後はcodeforcesやyukicoderに対応した環境も作るかもしれません。

Dockerに強い方やRustに詳しい方などは改善点やさらなる自動化できる点がありましたらプルリクをいただけると嬉しいです。問題点がありましたらコメントやgithubのissueにお願いします。有志で集まって便利な環境構築ができたらいいですね。

参考・関連記事

https://qiita.com/qryxip/items/bff57848ac9310d27f1a
https://magicode.io/shunnya0715/articles/9734ba4922894b329a756b6f3592e8ed
https://qiita.com/qryxip/items/b945c0adbd62ce5f1f3d
https://qiita.com/okaponta_/items/7e82de5d1f78f547fe4b
https://zenn.dev/23prime/articles/74cda5a096a3b3

广告
将在 10 秒后关闭
bannerAds