Docker+VSCodeのRust環境を作る
Docker+VSCodeのRust環境を作る
RustがCとPythonの間(C寄り)の中級型言語でCより書きやすく速度はCと大差ないらしいということで、触りたくなったのでWindowsでの環境構築を調べたメモ。
GUIアプリを作るようなケースでなければ以下の方法で問題ないはず。
- .devcontainerを使わない方法(1ファイルのみのコンパイル・実行なら可能)
- .devcontainerを使う方法(ワークスペース・パッケージ単位のビルド、1パッケージのデバッグ、補完やらが可能。基本的にはこっち)
環境・事前準備
VSCode(+拡張機能RemoteContainer)とDockerDesktopはインストール済みを想定
Rustはどんな言語か
- C/C++のような静的型付けの言語
- 高低レイヤ具合を比較するとPython > Go > Rust > C/C++ あたりで速度はこの逆になってくる
- 特徴として、メモリ安全性とスレッドセーフがよく言われる
- エラーメッセージが丁寧
- ユニットテストが標準機能でできる
- コンパイラは
rustc.rs
でRustで書かれているself-host型になる。- ここの解説がわかりやすい https://www.quora.com/Was-Rust-written-in-C-C
最初の方はOcamlあたりでコンパイルされていて途中からRust自身に変わったらしい。教師学習から自己学習に変わったと考えるとなかなか生物っぽくて面白い.低レイヤを知りたい人のためのCコンパイラ作成入門 でも触れてるがアセンブリの名残りがあるとすると生物学の祖先の遺伝子に近いものがコンパイラにもあるんだなあ。
- チュートリアル教科書:The Rust Programming Language 日本語版 The Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
インストールした環境では以下のコマンドが使えるようになる。
.devcontainerを使わない場合
これは公式のRustイメージでコンパイルする方法である。
まず、公式のRustイメージをインストールしておく。
docker pull rust:latest
VSCodeで以下のHello Worldのコードを書く。 なお直接Windows上にRustをインストールしてないのでマクロの補完は効かない。
// hello.rs fn main(){ println!("hello world!") }
ディレクトリ構造は
- Project - hello.rs
こいつをVSCode上のターミナルからrustc
コマンドでコンパイルする。
PS Project> docker run -it --rm -v "$(PWD):/usr/src/myapp" rust /bin/sh -c "rustc / usr/src/myapp/hello.rs; ./hello" hello world!
.devcontainerを使う場合
- 公式のRustイメージをインストールする必要なし(間接的にレイヤとしてインストールされるが)
- デバッグ可能(lldbデバッガ)
参考: VS Code + Remote Containers で快適な Dev ライフを | サン・エム・システム株式会社
- プロジェクト(ワークスペース)にするフォルダを右クリックしてVSCodeを開く
-
左下「Open a Remote Window」マークをクリック
Open a Remote Window - 「Reopen in Container」を選択。
Reopen in Container - ここで対応している言語が選べる。今回は「Rust」を選ぶ。
言語選択 -
ContainerのOSを選択。「buster」にする。
ContainerのOS選択 - 追加インストールするものを決める。今回はなし
オプションのインストール
以上全てを設定するとDLが始まる。
この手順で導入するとVSCode用Rustイメージ(公式のRustイメージがベースイメージ)がインストールされる。 (初期インストールは時間がかかる)
- pullされるイメージは https://hub.docker.com/_/microsoft-vscode-devcontainers の中の
mcr.microsoft.com/vscode/devcontainers/rust - bullseye, buster
になる。
また、VSCodeの拡張機能がnpmのローカルインストールと同じ感じでインストールされる。
devcontainerを閉じた後にもう1度開くには, 左下マークをクリックして「Open Folder in Container」を選択してプロジェクト用のフォルダを開いてあげればいい。
出来上がるファイル構成
インストールが終わると次のファイル構成になる。 VSCode側が生成したものとRust側が生成したものがあるので注意。
- Project - .git - .gitignore - .devcontainer - Dockerfile // このイメージがdocker pullされる - .vscode - launch.json // デバッガ用の構成ファイル // 👆ここまでがVSCode側で生成 // 👇ここからcargo initで生成されたファイル - src - main.rs // hello worldが書かれている - Cargo.toml // Rustのパッケージ情報など - Cargo.lock // Rustのworkspace内での依存関係の明示など
動作確認
Rust関連のコマンドが使えるか確認
vscode ➜ /workspaces/rust_project (main ✗) $ cargo -V cargo 1.58.0 (f01b232bc 2022-01-19)
vscode ➜ /workspaces/rust_project (main ✗) $ rustc -V rustc 1.58.1 (db9d1b20b 2022-01-20)
vscode ➜ /workspaces/rust_project (main) $ rustup --version 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.58.1 (db9d1b20b 2022-01-20)`
Rust workspaceの設定
上記の設定ではRustの1パッケージのファイル構成なので、 次のようなパッケージが2つあるようなworkspace用の構成に作り変える。
https://github.com/Cartman0/rust_workspace_sample
RustのWorkspaceについて: Cargoのワークスペース - プログラミング言語 Rust
- Project - Cargo.toml // workspace用の設定 - Cargo.lock // workpaceあたり1つもち,依存関係を書く - p1 // package1 - Cargo.toml // package1用の設定 - src - main.rs - lib.rs - p2 // package2 - Cargo.toml // package2用の設定 - src - main.rs // 今回はp1のlibも読み込めるようにする(依存関係)
まず、workspace用の設定を追加する。
workspace用(直下)のCargo.toml
ファイルには以下のように書いてどのpackageをもつか明示する。
// /Cargo.toml [workspace] members = [ "p1", "p2" ]
workspace用のCargo.lock
にはpackage同士の依存関係を書く。
このdependencies
を書かないと外側のパッケージのファイルを見れずエラーになる。
// /Cargo.lock [[package]] name = "p1" version = "0.1.0" [[package]] name = "p2" version = "0.1.0" dependencies = [ "p1", ]
次に各パッケージの設定を追加する。
p1ディレクトリを作った後にその中でcargo init
すればパッケージの初期設定が得られる。
(今回はライブラリファイルをもつのでcargo init --lib
でもいい)
Project> mkdir p1 cd p1 cargo init
p1/Cargo.toml
の中を確認する。
気をつけるべきは[package]
となっているかとname
と[dependencies]
の部分。
name
の部分がworkspaceの設定に使われる。今回は外部依存はないので[dependencies]
は空のままでいい。
// p1/Cargo.toml [package] name = "p1" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]
p1にはライブラリファイルを使いたいので以下のような公開モジュールにしておく。
// p1/src/lib.rs pub mod my{ pub fn hey(){ println!("hey!"); } }
p1/src/main.rs
はライブラリを読み込めるように以下のようにしておく。
use p1::my; fn main() { println!("Hello, world! p1"); my::hey(); }
次にp2
の設定をする。
外部のp1に依存するのでpathをCargo.toml
に設定する。
このとき読み込むときの名前を変えられる。
//p2/Cargo.toml [package] name = "p2" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] p1 = { path = "../p1" }
p2/main.rs
は次のようにする。外部ライブラリの読み込みもp1と同じように読み込める。
// p2/src/main.rs use p1::my; fn main() { my::hey(); println!("Hello, world! p2"); }
以上でソースの設定はok。
デバッグの設定
次にVSCodeでデバッグ・ビルドの設定をする。 設定ファイルは以下のように対応している。
- ビルドのみ:コマンドか
.vscode/tasks.json
- デバッグ:
.vscode/launch.json
workspaceのビルドはProject直下で
cargo build //or cargo build --workspace
実際に試すと以下のような結果が得られ、target
ディレクトリに実行ファイルが生成される。
vscode ➜ /workspaces/rust_project (main ✗) $ cargo build Compiling p2 v0.1.0 (/workspaces/rust_project/p2) Compiling p1 v0.1.0 (/workspaces/rust_project/p1) Finished dev [unoptimized + debuginfo] target(s) in 8.10s
パッケージごとにビルドしたい場合は-p <package name>
をつける。
vscode ➜ /workspaces/rust_project (main ✗) $ cargo build -p p1 Finished dev [unoptimized + debuginfo] target(s) in 0.09s
.vscode/launch.json
を設定すればデバッグできる。
ただし、VSCodeのlldbデバッガでは1つの実行ファイルしか追えないのでcargo build --workspace
のデバッグではエラーが出る。
なのでデバッグのlaunch.jsonはpackageごとに設定すればいい。
"configurations":
に追加していけばいい。
cargo
属性中のargs
属性の配列が実行されるコマンドになる。
また、"filter"の指定がないとエラーになる。
{ ..., "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug 'p1' in 'rust_project'", "cargo": { "args": [ "build", "-p", "p1", "--verbose" ], "filter": { "kind": "bin" } }, "args": [], "cwd": "${workspaceFolder}" }, ... ], ...
VSCodeの「Run and Debug」からデバッグを実行できる。
1度実行するとmain
関数上部にクリック可能なボタンが追加される。
ビルド用のタスク設定
デバッグしないのであればタスクとして、コマンドを.vscode/tasks.json
に設定できる。
例えば,先程のworkspace全体のビルドを登録するとき次のように追加すればいい。
{ ..., "tasks": [ { "label": "rust: cargo build workspace", "type": "cargo", "command": "build", "problemMatcher": [ "$rustc" ], "args": [ "--workspace", "--verbose" ], "group": "build", "presentation": { "reveal": "always", // Terminalパネルを開く "clear": true, // 実行前にTerminalをクリア }, "options": { } }, ...], ... }
"Terminal > Run Task"を選択すると登録したタスク一覧(labelに設定してタスク名)が出るので選ぶと実行される。。
実行結果:
> Executing task: cargo build --workspace --verbose < Fresh p2 v0.1.0 (/workspaces/rust_project/p2) Fresh p1 v0.1.0 (/workspaces/rust_project/p1) Finished dev [unoptimized + debuginfo] target(s) in 0.75s Terminal will be reused by tasks, press any key to close it.
参考リンク
[https://qiita.com/kishibashi3/items/e20aecef45ed8341e739:embed:cite]