本業仕事で新しいIoTプロジェクトに関わっている。このプロジェクトのターゲット機はRaspberry Piを搭載しており、小生はファームウェアの開発を行っているが、そのプログラミング言語としてRustを使っている。
本格的にRustを使ってソフトウェア開発を行うのがこれが初めてだ。ハード制御を行うファームウェアはC/C++を使って開発するのが一般的だが(最近は組み込みもPythonやNode.jsを使って開発できないこともないが、速度的制約をクリアしなければならなかったり細かなハード制御が必要だったりすると、やはりC/C++の方が実現し易い)、Rustを使っても同様の目的が達成できるんじゃないかと思っていた。実際にRustによるLinux稼働ファームウェアの開発を進めていて、この選択は正解だったと実感している。
RustはC/C++よりプログラミング・コストが低く、発達したエコシステムにより豊富なライブラリが揃っており、それらを使うとハード寄りのディープな目的もほとんど達成できる。速度面でもC/C++に劣らず、細やかな非同期処理、スレッド制御、メモリ管理も可能だ。
最近登場している開発系ツールはRustで書かれているものが多い。Rustを使ってWeb開発もできるし、PythonやNode.jsより応答性能の高いものが作れる。Rust用機械学習ライブラリも登場しているし、Rustで書かれたリアルタイムOSやESP32などのマイコン開発用フレームワークも存在する。組み込み開発もいまはRustでやるのが最適解なのかもしれない。あらゆる分野でRustが使われるトレンドが起きているようだ。
今回のプロジェクトだけでなく、自己研究でのIoTやエッジ・組み込みLinux利用開発でも積極的にRustを使っていくつもりだ。
自分の備忘録として、MacやUbuntu機へRustの開発環境を構築する手順を書き残しておく。
Rustのインストール
Rustのインストール方法として、公式サイトではrustupを使うことが推薦されている。
asdfを使ってRustをインストールすることもできるが、asdfによって導入できるのはrustc(Rustコンパイラ)だけで、rustupは使えない。rustupを使うと複数のバージョンのRustをインストールでき(asdfのような言語バージョン管理ツールの機能も兼ね備えている)、cargoというパッケージマネージャも一緒に導入できる。Rustでのパッケージ管理はこのcargoを使うことが前提となる。
現状、Rustのインストールはrustupを使って行うのが最良の方法と言えるだろう。
% brew install rustup
% echo 'export PATH="/usr/local/opt/rustup/bin:$PATH"' >> ~/.zshrc % source ~/.zshrc
bashの場合は、"~/.zshrc
" の部分を "~/.bashrc
" に変える。
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
本インストール・コマンドの実行時に後述の "rustup-init
" コマンドも一緒に実行される。
Rustの初期設定
rustupをインストールしたら、最初に下のコマンドを実行して、Rust環境の初期設定を行っておく。
% rustup-init Welcome to Rust! This will download and install the official compiler for the Rust programming language, and its package manager, Cargo. Rustup metadata and toolchains will be installed into the Rustup home directory, located at: /Users/LOGNAME/.rustup This can be modified with the RUSTUP_HOME environment variable. The Cargo home directory is located at: /Users/LOGNAME/.cargo This can be modified with the CARGO_HOME environment variable. The cargo, rustc, rustup and other commands will be added to Cargo's bin directory, located at: /Users/LOGNAME/.cargo/bin This path will then be added to your PATH environment variable by modifying the profile files located at: /Users/LOGNAME/.profile /Users/LOGNAME/.zshenv You can uninstall at any time with rustup self uninstall and these changes will be reverted. Current installation options: default host triple: x86_64-apple-darwin default toolchain: stable (default) profile: default modify PATH variable: yes 1) Proceed with standard installation (default - just press enter) 2) Customize installation 3) Cancel installation > info: profile set to 'default' info: default host triple is x86_64-apple-darwin info: syncing channel updates for 'stable-x86_64-apple-darwin' info: latest update on 2024-06-13, rust version 1.79.0 (129f3b996 2024-06-10) info: downloading component 'cargo' 6.9 MiB / 6.9 MiB (100 %) 4.8 MiB/s in 1s ETA: 0s info: downloading component 'clippy' info: downloading component 'rust-docs' 15.4 MiB / 15.4 MiB (100 %) 4.8 MiB/s in 3s ETA: 0s info: downloading component 'rust-std' 23.4 MiB / 23.4 MiB (100 %) 6.7 MiB/s in 3s ETA: 0s info: downloading component 'rustc' 55.5 MiB / 55.5 MiB (100 %) 5.1 MiB/s in 9s ETA: 0s info: downloading component 'rustfmt' info: installing component 'cargo' info: installing component 'clippy' info: installing component 'rust-docs' 15.4 MiB / 15.4 MiB (100 %) 2.5 MiB/s in 5s ETA: 0s info: installing component 'rust-std' 23.4 MiB / 23.4 MiB (100 %) 9.8 MiB/s in 2s ETA: 0s info: installing component 'rustc' 55.5 MiB / 55.5 MiB (100 %) 10.6 MiB/s in 5s ETA: 0s info: installing component 'rustfmt' info: default toolchain set to 'stable-x86_64-apple-darwin' stable-x86_64-apple-darwin installed - rustc 1.79.0 (129f3b996 2024-06-10) Rust is installed now. Great! 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). To configure your current shell, you need to source the corresponding env file under $HOME/.cargo. This is usually done by running one of the following (note the leading DOT): . "$HOME/.cargo/env" # For sh/bash/zsh/ash/dash/pdksh source "$HOME/.cargo/env.fish" # For fish
※上はIntel Mac上での "rustup-init
" コマンドの実行例。他のプラットホーム上では "x86_64-apple-darwin
" などの表示が変わってくる。
"rustup-init
" コマンドによる初期設定が済んだら、下のような記述を.zshrc
(bashの場合は.bashrc
)に追加した上で、それを有効にする。
% vi ~/.zshrc . "$HOME/.cargo/env" % source ~/.zshrc
その上で、コマンド "rustc
" と "cargo
" が利用できることを確認しておく。
% rustc --version rustc 1.79.0 (129f3b996 2024-06-10) % cargo --version cargo 1.79.0 (ffa9cf99a 2024-06-03)
rustupによってインストールしたRust環境のファイル群はディレクトリ$HOME/.cargo
の中に格納されている。
補足情報
Macで "rustup-init
" コマンドを実行したときに、下のような警告が表示されることがある。
% rustup-init warning: it looks like you have an existing installation of Rust at: warning: /usr/local/bin warning: It is recommended that rustup be the primary Rust installation. warning: Otherwise you may have confusion unless you are careful with your PATH warning: If you are sure that you want both rustup and your already installed Rust warning: then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes warning: or pass `-y' to ignore all ignorable checks. error: cannot install while Rust is installed Continue? (y/N)
Homebrewによってすでにrust
パッケージが導入済みときに、この警告が表示される。
この場合は、以下のコマンドによってrust
パッケージが存在することを確認し、それをアンインストールしてから、 "rustup-init
" コマンドを再度実行すれば良い。
% which rustc /usr/local/bin/rustc % brew info rust ==> rust: stable 1.79.0 (bottled), HEAD Safe, concurrent, practical language https://www.rust-lang.org/ Installed /usr/local/Cellar/rust/1.78.0 (3,320 files, 294.3MB) * Poured from bottle using the formulae.brew.sh API on 2024-06-07 at 08:29:53 From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/r/rust.rb License: Apache-2.0 or MIT ==> Dependencies Required: libgit2 ✔, libssh2 ✔, llvm ✘, openssl@3 ✘, pkg-config ✔ ==> Requirements Required: macOS >= 10.12 (or Linux) ✔ ==> Options --HEAD Install HEAD version ==> Caveats zsh completions have been installed to: /usr/local/share/zsh/site-functions ==> Analytics install: 36,888 (30 days), 122,724 (90 days), 431,118 (365 days) install-on-request: 26,090 (30 days), 89,554 (90 days), 325,780 (365 days) build-error: 346 (30 days) % brew uninstall rust
Rustによるプロジェクト作成とビルド
Rustによるプロジェクト(プログラム)の作成とビルドができることを確認しておこう。
プロジェクトの作成
新しいRustプロジェクトの作成は、以下のような "cargo
" コマンドを使って行える。
プロジェクト・ディレクトリを新規作成する場合
% cargo new hello_rust Creating binary (application) `hello_rust` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
既存ディレクトリ内でプロジェクトを作成する場合
% mkdir hello_rust % cd hello_rust % cargo init Creating binary (application) package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Rustプロジェクトを作成すると、一緒にCargo.toml
というファイルも生成され、この中にプロジェクトの依存パッケージ情報が記述されている(Rustでは、パッケージを "Crate" と呼称する)。
プロジェクト・ソースの作成
コマンド "cargo new PROJECT_NAME
" や "cargo init
" によってプロジェクトを作成すると、プロジェクト・ディレクトリ内にsrc
というディレクトリが生成されており、Rustプログラムのソースファイルはこのディレクトリの中に作成していくことが決まり事になっている。
また、下のような一つのソースファイルも自動的に生成される。
src/main.rs
fn main() { println!("Hello, world!"); }
プロジェクトのビルドと実行
プロジェクト・ディレクトリ内で下のコマンドを実行することで、そのプロジェクトのビルドが行える。
% cargo build Compiling hello_rust v0.1.0 (/Users/LOGNAME/hello_rust) Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.18s
下のコマンドによって、プロジェクト・プログラムを起動・実行することができる。
% cargo run Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s Running `target/debug/hello_rust` Hello, world!
コマンド "cargo run
" はプロジェクトのビルドと実行を続けて行っている。
Rust開発環境の整備
Rustによるプログラム開発に有益なツールが多く在るが、その中から厳選してみた。それらのインストール方法を以下に記しておく。
追加コンポーネントのインストール
% rustup component add rls rust-analysis rust-src rust-analyzer
各コンポーネントの機能は以下のとおり。
rls
旧版のRust Language Server(古いバージョンのRustで使われる)rust-analysis
Rust標準ライブラリのメタデータrust-src
Rust標準ライブラリのソースコードのローカルコピー(rls
やrust-analyzer
などで参照される)rust-analyzer
新版のRust Language Server(最近のバージョンのRustでは、こちらが使われる)
以下のコンポーネントも有益だが、本記事執筆時点のRustの最新バージョンでは、これらは "rustup-init
" コマンドによる初期設定時にインストール済みになっていた。
clippy
Rust公式のリンター(変数名ルールなどの静的解析を行う)rust-docs
公式Rustドキュメントのローカルコピー("rustup doc
" コマンド実行時に、ローカルの公式ドキュメントを参照できるようになる)rustfmt
コード・フォーマッタ(ソースコードの可読性整形を行う)
インストール済みのRustコンポーネントは下のコマンドによって確認できる。
% rustup component list cargo-x86_64-apple-darwin (installed) clippy-x86_64-apple-darwin (installed) llvm-bitcode-linker-x86_64-apple-darwin llvm-tools-x86_64-apple-darwin rls-x86_64-apple-darwin rust-analysis-x86_64-apple-darwin rust-analyzer-x86_64-apple-darwin rust-docs-x86_64-apple-darwin (installed) rust-src rust-std-aarch64-apple-darwin rust-std-aarch64-apple-ios rust-std-aarch64-apple-ios-sim rust-std-aarch64-linux-android rust-std-aarch64-pc-windows-gnullvm rust-std-aarch64-pc-windows-msvc rust-std-aarch64-unknown-fuchsia rust-std-aarch64-unknown-linux-gnu rust-std-aarch64-unknown-linux-musl rust-std-aarch64-unknown-linux-ohos rust-std-aarch64-unknown-none rust-std-aarch64-unknown-none-softfloat rust-std-aarch64-unknown-uefi rust-std-arm-linux-androideabi rust-std-arm-unknown-linux-gnueabi rust-std-arm-unknown-linux-gnueabihf rust-std-arm-unknown-linux-musleabi rust-std-arm-unknown-linux-musleabihf rust-std-armebv7r-none-eabi rust-std-armebv7r-none-eabihf rust-std-armv5te-unknown-linux-gnueabi rust-std-armv5te-unknown-linux-musleabi rust-std-armv7-linux-androideabi rust-std-armv7-unknown-linux-gnueabi rust-std-armv7-unknown-linux-gnueabihf rust-std-armv7-unknown-linux-musleabi rust-std-armv7-unknown-linux-musleabihf rust-std-armv7-unknown-linux-ohos rust-std-armv7a-none-eabi rust-std-armv7r-none-eabi rust-std-armv7r-none-eabihf rust-std-i586-pc-windows-msvc rust-std-i586-unknown-linux-gnu rust-std-i586-unknown-linux-musl rust-std-i686-linux-android rust-std-i686-pc-windows-gnu rust-std-i686-pc-windows-gnullvm rust-std-i686-pc-windows-msvc rust-std-i686-unknown-freebsd rust-std-i686-unknown-linux-gnu rust-std-i686-unknown-linux-musl rust-std-i686-unknown-uefi rust-std-loongarch64-unknown-linux-gnu rust-std-loongarch64-unknown-none rust-std-loongarch64-unknown-none-softfloat rust-std-nvptx64-nvidia-cuda rust-std-powerpc-unknown-linux-gnu rust-std-powerpc64-unknown-linux-gnu rust-std-powerpc64le-unknown-linux-gnu rust-std-riscv32i-unknown-none-elf rust-std-riscv32im-unknown-none-elf rust-std-riscv32imac-unknown-none-elf rust-std-riscv32imafc-unknown-none-elf rust-std-riscv32imc-unknown-none-elf rust-std-riscv64gc-unknown-linux-gnu rust-std-riscv64gc-unknown-none-elf rust-std-riscv64imac-unknown-none-elf rust-std-s390x-unknown-linux-gnu rust-std-sparc64-unknown-linux-gnu rust-std-sparcv9-sun-solaris rust-std-thumbv6m-none-eabi rust-std-thumbv7em-none-eabi rust-std-thumbv7em-none-eabihf rust-std-thumbv7m-none-eabi rust-std-thumbv7neon-linux-androideabi rust-std-thumbv7neon-unknown-linux-gnueabihf rust-std-thumbv8m.base-none-eabi rust-std-thumbv8m.main-none-eabi rust-std-thumbv8m.main-none-eabihf rust-std-wasm32-unknown-emscripten rust-std-wasm32-unknown-unknown rust-std-wasm32-wasi rust-std-wasm32-wasip1 rust-std-wasm32-wasip1-threads rust-std-x86_64-apple-darwin (installed) rust-std-x86_64-apple-ios rust-std-x86_64-fortanix-unknown-sgx rust-std-x86_64-linux-android rust-std-x86_64-pc-solaris rust-std-x86_64-pc-windows-gnu rust-std-x86_64-pc-windows-gnullvm rust-std-x86_64-pc-windows-msvc rust-std-x86_64-unknown-freebsd rust-std-x86_64-unknown-fuchsia rust-std-x86_64-unknown-illumos rust-std-x86_64-unknown-linux-gnu rust-std-x86_64-unknown-linux-gnux32 rust-std-x86_64-unknown-linux-musl rust-std-x86_64-unknown-linux-ohos rust-std-x86_64-unknown-netbsd rust-std-x86_64-unknown-none rust-std-x86_64-unknown-redox rust-std-x86_64-unknown-uefi rustc-x86_64-apple-darwin (installed) rustc-dev-aarch64-apple-darwin rustc-dev-aarch64-pc-windows-msvc rustc-dev-aarch64-unknown-linux-gnu rustc-dev-aarch64-unknown-linux-musl rustc-dev-arm-unknown-linux-gnueabi rustc-dev-arm-unknown-linux-gnueabihf rustc-dev-armv7-unknown-linux-gnueabihf rustc-dev-i686-pc-windows-gnu rustc-dev-i686-pc-windows-msvc rustc-dev-i686-unknown-linux-gnu rustc-dev-loongarch64-unknown-linux-gnu rustc-dev-powerpc-unknown-linux-gnu rustc-dev-powerpc64-unknown-linux-gnu rustc-dev-powerpc64le-unknown-linux-gnu rustc-dev-riscv64gc-unknown-linux-gnu rustc-dev-s390x-unknown-linux-gnu rustc-dev-x86_64-apple-darwin rustc-dev-x86_64-pc-windows-gnu rustc-dev-x86_64-pc-windows-msvc rustc-dev-x86_64-unknown-freebsd rustc-dev-x86_64-unknown-illumos rustc-dev-x86_64-unknown-linux-gnu rustc-dev-x86_64-unknown-linux-musl rustc-dev-x86_64-unknown-netbsd rustc-docs-x86_64-unknown-linux-gnu rustfmt-x86_64-apple-darwin (installed)
追加パッケージのインストール
% cargo install cargo-edit % cargo install cargo-watch
$ sudo apt install -y libssl-dev $ cargo install cargo-edit $ cargo install cargo-watch
cargo-edit
(Rustでのプロジェクトの依存パッケージ変更はCargo.toml
を編集することで行うが)Rustパッケージ(Crate)の追加/削除を "cargo add CRATE_NAME
" や "cargo rm CRATE_NAME
" というコマンドで行えるようになるcargo-watch
ソースファイルの変更を検知して、"cargo run
" コマンドを自動的に実行してくれる
インストール済みのRustパッケージは下のコマンドによって確認できる。
% cargo install --list cargo-edit v0.12.3: cargo-add cargo-rm cargo-set-version cargo-upgrade cargo-watch v8.5.2: cargo-watch
Visual Studio Code拡張機能のインストール
VSCodeでのRustプログラム開発に有益な拡張機能として、rust-analyzer
をインストールしておくことを薦める。
これは上述のコンポーネントrust-analyzer
のVSCode版だ。
少し前までRust
(Rust言語対応ユーティリティ機能)とrust-analyzer
(Language Support Server)という2つの拡張機能が存在しており、この両方をインストールすることが推薦されていたが、現在この2つはrust-analyzer
の一つに統合されている。本記事執筆時点では、VSCodeのRust用拡張機能として最低限rust-analyzer
だけをインストールしておけば良いだろう。
【追記】〔2024-07-23〕
VSCodeのRust用拡張機能として、CodeLLDB
もインストールする必要があるのを書き忘れていた。
これはVSCode上でのRustソースコードのデバッグ実行に使われるものだ。
小生のVSCode環境にはSwift
という拡張機能が導入済みで、このSwift
がCodeLLDB
に依存しており、すでにCodeLLDB
がインストール済みだったので上のことに気づかなかった。
【追記】〔2024-07-24〕
備忘録として、cargo
コマンドの使い方も書いておく。
新規プロジェクトの作成
cargo new PROJECT_NAME
本コマンドは通常パッケージ用プロジェクトを作成する。
コマンド "cargo new --bin PROJECT_NAME
" によって、バイナリパッケージ用プロジェクトを作成できる。
既存ディレクトリ上でのプロジェクトの作成
cd PROJECT_DIR cargo init
プロジェクトのビルド
cargo build
オプションなし(または--debug
オプション付き)で本コマンドを実行すると、Debug用実行可能ファイルがビルドされる。コマンド "cargo build --relaese
" だと、Relaese用実行可能ファイルがビルドされる。
コマンド "cargo build --example EXAMPLE_NAME
" によって、examples
ディレクトリ内の指定サンプルプログラムをビルドできる。
プロジェクトのビルドと実行
cargo run
コマンド "cargo run --example EXAMPLE_NAME
" によって、examples
ディレクトリ内の指定サンプルプログラムをビルド・実行できる。
プロジェクトのビルド可能確認
cargo check
本コマンドによってプロジェクトのビルドは行われず、コンパイル確認だけが行われる。
プロジェクトのビルド削除(クリーン)
cargo clean
パッケージ(Crate)のインストール
cargo add CRATE_NAME
※cargo-edit
がインストールされている場合に使えるコマンド
パッケージ(Crate)のアンインストール
cargo rm CRATE_NAME
※cargo-edit
がインストールされている場合に使えるコマンド
パッケージ(Crate)の検索
cargo search CRATE_NAME
Rust Crateの公式配布サイトcrates.ioから、指定した名前を含むCrateが検索される。
依存Crateの更新
cargo update
本コマンドによって、プロジェクトの全依存Crateが最新版へ更新される。
テストの実行
cargo test
ディレクトリtests
内にプロジェクトの単体テスト用ソースファイルが作成されている場合に、本コマンドによってそれらがビルド・実行される。
プロジェクト・ドキュメントの作成
cargo doc
プロジェクトのファイル群からドキュメントを生成する。
ソースファイル中に一定のルールに則って記述されたコメントが、本コマンドによるドキュメント情報収集の対象となる。
バイナリパッケージのインストール
cargo install PACKAGE_NAME
バイナリパッケージのアンインストール
cargo uninstall PACKAGE_NAME
【参照リンク】