Cargo 專案設定(Cargo.toml)
簡介
在 Rust 生態系統中,Cargo 是唯一官方的建置與套件管理工具。它不只負責編譯、測試與發佈,還透過 Cargo.toml 檔案提供完整的專案設定。掌握 Cargo.toml 的寫法與慣例,等於擁有了管理相依、版本、功能旗標、工作區等所有專案資訊的鑰匙。
對於剛踏入 Rust 的新手而言,Cargo.toml 看起來只是一個簡單的文字檔,但它實際上是 Cargo 與 Rust 編譯器之間的協議。若能正確設定,開發流程會變得流暢;若設定不當,則可能在相依衝突、編譯失敗或發佈錯誤上耗費大量時間。本文將一步步說明 Cargo.toml 的核心概念,並提供實務範例,協助讀者從「會用」升級到「會寫」的階段。
核心概念
1. 基本結構
Cargo.toml 採用 TOML(Tom's Obvious, Minimal Language) 格式,分為多個表格(table),每個表格以方括號 [] 包住。最常見的表格包括 [package]、[dependencies]、[dev-dependencies]、[features] 等。
[package]
name = "my_app" # 套件名稱,Cargo 會以此建立 crate 名稱
version = "0.1.0" # 依照 semver 規則的版本號
edition = "2021" # Rust edition,建議使用最新的 2021
authors = ["Alice <alice@example.com>"]
description = "示範 Cargo.toml 設定的範例專案"
license = "MIT"
Tip:
edition只需要在第一次建立專案時指定,之後若要升級到更高的 edition,需要手動修改並確保程式碼相容。
2. 相依套件 ([dependencies])
相依套件是專案最重要的部分。可以指定 版本範圍、來源(crates.io、Git、路徑)以及 可選功能。
[dependencies]
serde = "1.0" # 只要符合 1.0.x 的最新版本
rand = { version = "0.8", features = ["std"] } # 同時啟用 std 功能
log = { git = "https://github.com/rust-lang/log.git", rev = "a1b2c3d" } # 從 Git 取得特定提交
my_local_lib = { path = "../my_local_lib" } # 使用相對路徑的本地 crate
- 版本範圍:
"1.0"等同於^1.0,代表「兼容 1.x 但不跨越 2.0」;若想鎖定精確版本,可寫"=1.0.3"。 - Git 相依:
rev、tag、branch任選其一,Cargo 會根據提供的資訊檢出對應的 commit。
3. 開發相依 ([dev-dependencies])
僅在測試、範例或 cargo bench 時需要的套件,放在 dev-dependencies,不會被最終二進位檔帶入。
[dev-dependencies]
assert_cmd = "2.0"
tempfile = "3.3"
4. 功能旗標 ([features])
功能旗標讓 crate 可以 條件式地啟用 可選相依或程式碼區塊。每個功能本質上是一組相依的集合。
[features]
default = ["json"] # 預設啟用的功能
json = ["serde_json"] # 啟用 json 時會自動拉入 serde_json
tls = ["native-tls"] # TLS 功能
在程式碼中使用:
#[cfg(feature = "json")]
fn parse_json(s: &str) -> serde_json::Result<serde_json::Value> {
serde_json::from_str(s)
}
5. 工作區 ([workspace])
當專案包含多個子 crate 時,使用 工作區 讓 Cargo 共享 target 目錄與相依解析,提升建置速度。
[workspace]
members = [
"core",
"cli",
"utils",
]
每個子 crate 仍保有自己的 Cargo.toml,但根目錄的 Cargo.toml 僅負責宣告工作區成員與共用設定。
6. 替代相依 ([patch]、[replace])
在測試新版本或臨時覆寫相依時,可使用 patch 或 replace。
[patch.crates-io]
serde = { git = "https://github.com/serde-rs/serde.git", branch = "master" }
此設定會讓所有依賴 serde 的 crate 改為使用指定的 Git 分支。
程式碼範例
範例 1:最小可執行的 Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[dependencies]
只要
cargo run就能產生Hello, world!。
範例 2:啟用可選功能的相依
[dependencies]
serde = { version = "1.0", features = ["derive"] }
[features]
default = [] # 不自動啟用任何功能
json = ["serde_json"] # 手動啟用時會拉入 serde_json
在 Cargo.toml 中加入 --features json,即可在程式碼中使用 serde_json。
範例 3:工作區與共享相依
# 根目錄 Cargo.toml
[workspace]
members = ["app", "lib"]
[dependencies]
log = "0.4" # 工作區所有成員共用此相依
# app/Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"
[dependencies]
lib = { path = "../lib" } # 依賴工作區內的 lib
# lib/Cargo.toml
[package]
name = "lib"
version = "0.1.0"
edition = "2021"
[dependencies]
log = { version = "0.4", optional = true } # 讓 lib 可以在不需要 log 時被使用
範例 4:使用 Git 相依與特定 commit
[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git", rev = "a1b2c3d4e5" }
此設定確保每次建置都使用相同的 commit,避免因上游變更導致不預期的錯誤。
範例 5:Patch 取代 crates.io 版本(臨時測試)
[patch.crates-io]
tokio = { git = "https://github.com/tokio-rs/tokio.git", branch = "master" }
在本地測試新功能時,不必等官方發佈新版本。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案 / 最佳實踐 |
|---|---|---|
版本範圍寫錯(如 1.0 與 ^1.0 混淆) |
Cargo 可能自動升級到不相容的次要版本,導致編譯錯誤。 | 明確使用 ^、~ 或 =,必要時鎖定精確版本。 |
功能旗標未列入 default |
使用 cargo build 時功能不會被啟用,導致缺少相依。 |
在 [features] 中將常用功能加入 default,或在 CI 中明確指定 --features。 |
| 工作區成員路徑錯誤 | cargo build 找不到子 crate,報錯 could not find Cargo.toml。 |
使用相對路徑或絕對路徑,並在根目錄的 members 中保持一致。 |
| Git 相依未鎖定 commit | 每次建置可能拉到不同的 commit,產生不可重現的錯誤。 | 使用 rev、tag 或 branch(配合 CI)來固定版本。 |
[replace] 與 [patch] 同時使用 |
兩者的行為衝突,導致 Cargo 無法解析相依圖。 | 只保留 patch(較新且較安全),除非確定需要 replace 的特殊情境。 |
最佳實踐
- 使用
cargo audit定期檢查相依的安全性。 - 在 CI 中鎖定功能旗標:
cargo test --all-features,確保所有可選功能都能通過測試。 - 保持
Cargo.lock:對於應用程式(binary)必須提交Cargo.lock,確保部署環境與開發環境一致。對於 library 則可不提交。 - 利用
cargo metadata觀察相依圖,快速定位衝突。
實際應用場景
- 微服務專案:使用工作區管理
api-gateway、auth-service、data-service三個子 crate,所有服務共享log、serde等基礎相依,減少重複編譯時間。 - CLI 工具 + Library:將核心演算法抽成
my_lib,CLI 包裝在my_cli中。透過[features]提供json、yaml輸出選項,使用者可自行決定是否安裝額外的序列化套件。 - 嵌入式開發:在
Cargo.toml中加入default = ["std"]與no_std功能,讓同一套程式碼同時支援桌面與裸機環境。 - 第三方套件測試:開發新功能時,使用
[patch.crates-io]暫時把tokio指向 Git master,驗證相容性後再回到正式發佈版。
總結
Cargo.toml 不只是記錄套件名稱與版本的檔案,它是 Rust 專案的配置中心,掌握其語法與慣例能讓開發者在相依管理、功能切換、工作區協作上事半功倍。本文從基本結構、相依設定、功能旗標、工作區、以及進階的 patch/replace 機制,提供了完整且實務導向的說明與範例。
記得:在寫
Cargo.toml時,清晰、可重現 是最重要的原則。透過嚴謹的版本鎖定、功能預設與 CI 測試,你的 Rust 專案將能在團隊協作與持續部署中保持穩定與可維護。祝你在 Cargo 的世界裡開發順利!