Rust 入門與環境設置 ── 編譯與執行流程
簡介
在學習任何程式語言時,編譯與執行的流程是最先必須掌握的基礎概念。對於 Rust 這樣的系統程式語言,了解編譯器(rustc)與建置工具(cargo)如何協同工作,不僅能幫助你快速寫出可執行的程式,還能在日後的效能優化與錯誤除錯上奠定堅實的基礎。
本篇文章將從最簡單的單檔編譯說起,逐步延伸到使用 Cargo 進行專案管理、Debug 與 Release 兩種建置模式,以及常見的環境變數設定,讓讀者在實作時能夠 得心應手。
核心概念
1. rustc:單檔編譯器
rustc 是 Rust 官方提供的底層編譯器,直接將 .rs 檔案編譯成可執行檔或函式庫。它接受多種指令列參數,讓開發者可以自行控制最佳化等級、目標平台等。
1.1 基本編譯指令
// hello.rs
fn main() {
println!("Hello, Rust!");
}
$ rustc hello.rs # 產生同目錄下的 `hello` 可執行檔
$ ./hello
Hello, Rust!
rustc hello.rs會使用預設的 debug 設定(opt-level = 0),編譯速度快、除錯資訊完整。- 若想啟用最佳化,只需要加上
-C opt-level=3:
$ rustc -C opt-level=3 hello.rs -o hello_opt
$ ./hello_opt
Hello, Rust!
1.2 指定目標平台
$ rustc --target=x86_64-unknown-linux-gnu hello.rs
這在交叉編譯(cross‑compilation)時非常有用,例如在 Windows 上產生 Linux 可執行檔。
2. cargo:專案建置與套件管理
雖然 rustc 能直接編譯單檔程式,但實際開發往往牽涉多個模組、相依套件與測試。Cargo 為 Rust 官方的建置系統與套件管理工具,提供以下核心功能:
| 功能 | 常用指令 | 說明 |
|---|---|---|
| 建立新專案 | cargo new my_proj |
產生包含 Cargo.toml 與 src/main.rs 的範本 |
| 編譯 (Debug) | cargo build |
產生 target/debug 目錄,含除錯資訊 |
| 編譯 (Release) | cargo build --release |
產生 target/release,啟用最高等級最佳化 |
| 執行程式 | cargo run |
先編譯再執行,等同 cargo build && ./target/debug/... |
| 測試 | cargo test |
執行內建測試框架 |
| 套件相依 | cargo add serde |
自動把依賴寫入 Cargo.toml 並下載 |
2.1 Cargo.toml 基本結構
[package]
name = "my_proj"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
[package]區塊描述專案本身的資訊。[dependencies]列出所有相依套件,Cargo 會自動解析版本衝突。
2.2 多檔案模組化
// src/main.rs
mod greeter; // 引入 greeter.rs
fn main() {
greeter::hello();
}
// src/greeter.rs
pub fn hello() {
println!("Hello from greeter module!");
}
使用
mod關鍵字即可把程式碼拆分成多個檔案,Cargo 會在編譯時自動把它們串接起來。
3. Debug vs. Release:何時使用哪個
| 項目 | Debug (cargo build) |
Release (cargo build --release) |
|---|---|---|
| 最佳化等級 | opt-level = 0(無最佳化) |
opt-level = 3(最高最佳化) |
| 編譯速度 | 快 | 較慢 |
| 可執行檔大小 | 較大 | 較小 |
| 執行效能 | 較差 | 最佳 |
| 除錯資訊 | 完整(包含符號表) | 只保留必要資訊 |
實務建議:開發階段使用 Debug,提交測試或正式部署前切換到 Release,以確保效能與二進位大小皆符合需求。
4. 環境變數與自訂建置腳本
Cargo 允許透過環境變數調整編譯行為,例如:
$ export RUSTFLAGS="-C target-cpu=native"
$ cargo build --release
RUSTFLAGS會在每次呼叫rustc時自動帶入,常用於 針對特定 CPU 做最佳化。- 若需要更複雜的前置或後置處理,可在專案根目錄加入
build.rs,Cargo 會在編譯前執行它。
// build.rs
fn main() {
println!("cargo:rerun-if-changed=src/config.toml");
// 例如根據 config.toml 產生 Rust 常數
}
程式碼範例
範例 1:使用 cargo run 快速測試
// src/main.rs
fn main() {
// 📌 這裡使用了 *format!* 宏,編譯時會自動展開成字串
let greeting = format!("Hello, {}!", "Rust");
println!("{}", greeting);
}
$ cargo run
Compiling my_proj v0.1.0 (/path/to/my_proj)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
Running `target/debug/my_proj`
Hello, Rust!
範例 2:切換 Debug / Release 並測量執行時間
// src/main.rs
use std::time::Instant;
fn fib(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fib(n - 1) + fib(n - 2),
}
}
fn main() {
let start = Instant::now();
let result = fib(40); // 計算較大的費波那契數
let duration = start.elapsed();
println!("fib(40) = {}", result);
println!("執行時間: {:.2?}", duration);
}
# Debug 版
$ cargo run
# Release 版
$ cargo run --release
觀察兩次執行時間差異,可明顯感受到 Release 的效能提升。
範例 3:使用 build.rs 產生編譯時常數
# Cargo.toml
[package]
name = "build_example"
version = "0.1.0"
edition = "2021"
build = "build.rs"
// build.rs
use std::env;
use std::fs;
use std::path::Path;
fn main() {
// 讀取專案根目錄下的 VERSION 檔案
let version = fs::read_to_string("VERSION")
.expect("無法讀取 VERSION 檔案")
.trim()
.to_string();
// 把版本資訊寫入環境變數,供程式碼使用
println!("cargo:rustc-env=PROJECT_VERSION={}", version);
}
// src/main.rs
fn main() {
// 透過 env! 巨集在編譯期取得環境變數
let version = env!("PROJECT_VERSION");
println!("此程式的版本是 {}", version);
}
$ echo "1.2.3" > VERSION
$ cargo run
此程式的版本是 1.2.3
範例 4:交叉編譯產生 Windows 可執行檔(在 Linux 主機上)
# 安裝目標平台的工具鏈
$ rustup target add x86_64-pc-windows-gnu
# 使用 cargo 交叉編譯
$ cargo build --target x86_64-pc-windows-gnu --release
產生的檔案位於 target/x86_64-pc-windows-gnu/release/your_proj.exe,可直接在 Windows 上執行。
範例 5:自訂 RUSTFLAGS 以啟用 LTO(Link Time Optimization)
$ export RUSTFLAGS="-C lto=yes"
$ cargo build --release
- LTO 能在連結階段進一步優化程式碼,通常會讓 Release 版的執行效能提升 5%~15%。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 忘記切換至 Release | 在效能測試時仍使用 cargo run(Debug) |
明確使用 cargo run --release 或 cargo build --release |
| 相依套件版本衝突 | 多個套件要求不同的 serde 版本 |
使用 cargo tree 檢查依賴樹,必要時手動指定相容版本 |
| 環境變數未傳遞 | 在 IDE 中執行時 RUSTFLAGS 不生效 |
在 IDE 的執行設定中加入環境變數,或在 .cargo/config.toml 中設定 |
| 交叉編譯缺少 linker | 目標平台的 linker 未安裝導致編譯失敗 | 安裝對應的交叉 linker(如 mingw-w64)或使用 rustup target add 後的預設工具鏈 |
過度使用 unsafe |
在不必要的地方加入 unsafe 會失去 Rust 的安全保證 |
僅在確定需要且已做好安全審查時 使用 unsafe,並加上詳細註解說明原因 |
最佳實踐
- 保持 Cargo.toml 整潔:只列出實際使用的依賴,避免「過度依賴」造成編譯時間膨脹。
- 使用工作區(workspace):當專案規模變大時,將多個相關 crate 放入同一個 workspace,能共享
target目錄、減少重複建置。 - 啟用
cargo fmt與cargo clippy:自動格式化與靜態分析可以在編譯前捕捉潛在錯誤。$ cargo fmt # 格式化程式碼 $ cargo clippy # 靜態分析,建議加上 `-- -D warnings` 把警告視為錯誤 - 持續集成(CI):在 GitHub Actions、GitLab CI 等平台上加入
cargo test、cargo build --release,確保每次提交都能順利編譯與通過測試。
實際應用場景
| 場景 | 為何需要了解編譯與執行流程 | 典型做法 |
|---|---|---|
| 嵌入式開發(如 ARM Cortex-M) | 必須交叉編譯至目標 MCU,且往往需要 no‑std 環境 | rustup target add thumbv7em-none-eabihf → cargo build --release --target thumbv7em-none-eabihf |
| WebAssembly (Wasm) 前端 | Rust 程式碼要編譯成 .wasm,再由 JavaScript 呼叫 |
cargo install wasm-pack → wasm-pack build --target web |
| 高頻交易或遊戲引擎 | 需要極致效能,必須使用 Release + LTO + target‑cpu=native | export RUSTFLAGS="-C opt-level=3 -C target-cpu=native -C lto=yes" → cargo build --release |
| CLI 工具 | 發布到 crates.io 前,需要同時提供 Linux、macOS、Windows 三平台的二進位 | 使用 cross(Docker + Cargo)或 GitHub Actions 產生多平台的 Release 檔案 |
| 大型服務端微服務 | 每個微服務都是獨立的 Cargo crate,使用 workspace 共享依賴與建置快取 | cargo workspace + docker build → cargo run --release 於容器內執行 |
總結
- 編譯與執行流程 是 Rust 開發的根基:從單檔
rustc到全功能的cargo,每一步都提供了不同層次的抽象與便利。 - 了解 Debug vs. Release、環境變數、交叉編譯 以及 自訂建置腳本,能讓你在不同的專案需求下快速切換、優化與部署。
- 避免常見的陷阱、遵循最佳實踐,並把 測試、格式化、靜態分析 融入日常開發流程,才能真正發揮 Rust 所提供的安全與效能保證。
掌握了本文的概念與範例,你就能在 本機、容器、甚至嵌入式設備 上自如地編譯、執行與部署 Rust 程式,為後續深入學習所有權、借用與異步編程奠定堅實的基礎。祝你在 Rust 的旅程中玩得開心、寫得順手! 🚀