Rust – 專案管理與 Cargo
主題:發布與版本控制
簡介
在 Rust 生態系統中,Cargo 不只是編譯、測試與依賴管理的工具,它同時負責套件(crate)的發布與版本控制。一個好的發佈流程可以讓你的程式庫被其他開發者安全、可靠地使用;而正確的版本管理則能避免相容性問題、降低維護成本。
本篇文章將帶你從基礎概念出發,說明如何在 Cargo 中設定版本、使用 Git 追蹤變更、將套件上傳至 crates.io,並提供實務範例與常見陷阱,協助你在專案開發的每個階段都保持一致性與可預測性。
核心概念
1. Cargo.toml 與版本號
Cargo.toml 是每個 Rust 專案的「說明書」,其中 package.version 欄位即決定了套件的版本號。Rust 社群採用 Semantic Versioning (SemVer),格式為 MAJOR.MINOR.PATCH:
| 類型 | 變更說明 | 版本遞增方式 |
|---|---|---|
| 破壞性變更 (breaking) | 公開 API 不相容 | MAJOR +1,MINOR、PATCH 皆歸 0 |
| 向下相容的功能 (feature) | 新增功能但不破壞舊有 API | MINOR +1,PATCH 歸 0 |
| 錯誤修正 (bugfix) | 只修正錯誤,不影響功能 | PATCH +1 |
# Cargo.toml 範例
[package]
name = "awesome_lib"
version = "1.2.3" # <─ 依照 SemVer 規則維護
authors = ["Alice <alice@example.com>"]
edition = "2021"
description = "一個示範用的 Rust 函式庫"
license = "MIT"
repository = "https://github.com/alice/awesome_lib"
小技巧:在開發階段可使用
0.x.y作為「尚未穩定」的版本,讓使用者知道 API 仍可能變動。
2. Git 與 Cargo 的結合
Cargo 內建對 Git 的支援,讓你可以:
- 在
Cargo.toml中直接引用 Git 位置 - 自動在
cargo publish前檢查 Git 狀態(必須在乾淨的 commit 上)
# 直接依賴 Git 上的 crate
[dependencies]
my_util = { git = "https://github.com/bob/my_util", rev = "a1b2c3d" }
發布前的 Git 檢查
# 確保工作目錄乾淨,沒有未提交的變更
git status --porcelain
# 若輸出為空,表示乾淨;否則請先 commit 或 stash
重要:
cargo publish會自動檢查當前 commit 是否已被 tag,若未 tag,建議先建立一個符合 SemVer 的 Git 標籤。
# 建立並推送標籤
git tag -a v1.2.3 -m "Release 1.2.3"
git push origin v1.2.3
3. 發布到 crates.io
發布流程非常簡單,只要執行 cargo publish,Cargo 會將程式碼、Cargo.toml 與相關檔案上傳至 crates.io。在此之前,需要先在 crates.io 取得 API token,並將它寫入 ~/.cargo/credentials。
# 1. 產生 token(在 crates.io 網站的 Account > Tokens)
# 2. 設定 token
cargo login <your-token>
# 3. 發布套件
cargo publish
注意:若
Cargo.toml中的publish = false,則套件不會被允許上傳;若要暫時禁止發布,可在[package]加上此欄位。
4. 使用 cargo release 自動化版本與 Git 標籤
手動調整版本、建立 tag、推送至遠端是一件繁瑣且易錯的事。社群提供的 cargo-release 工具可以一次完成:
# 安裝 cargo-release
cargo install cargo-release
# 以 patch 方式發布
cargo release patch
cargo release 會自動:
- 更新
Cargo.toml中的版本號 - 建立 Git commit 與 tag(如
v1.2.4) - 推送至遠端
- 執行
cargo publish
5. 多套件工作區(Workspace)與統一版本管理
大型專案往往包含多個子 crate。使用 Workspace 可以在同一個 Git repository 中管理多個套件,且可以一次性更新所有子 crate 的版本。
# 根目錄的 Cargo.toml
[workspace]
members = [
"core",
"cli",
"utils",
]
# 共享的版本設定(可在根目錄設定)
[workspace.package]
version = "0.1.0"
在工作區內,執行 cargo publish -p core 只會發布 core 這個子 crate;若想一次發布全部,則使用 cargo workspaces publish(需要額外工具)。
程式碼範例
範例 1:自動檢查 Git 狀態與版本
// build.rs – 在編譯前檢查 Git 狀態
use std::process::Command;
fn main() {
// 取得最近一次 commit hash
let output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.output()
.expect("無法執行 git");
let commit = String::from_utf8_lossy(&output.stdout);
println!("cargo:rustc-env=GIT_COMMIT={}", commit.trim());
// 確保工作目錄乾淨
let status = Command::new("git")
.args(&["status", "--porcelain"])
.output()
.unwrap();
if !status.stdout.is_empty() {
panic!("Git 工作目錄不乾淨,請先 commit 或 stash 變更");
}
}
說明:此程式會在
cargo build時自動執行,若有未提交的變更會直接中止,避免不小心發布未完成的程式碼。
範例 2:在 Cargo.toml 中使用條件依賴(feature)
[features]
default = ["serde"]
serde = ["serde_json"]
[dependencies]
serde_json = { version = "1.0", optional = true }
// lib.rs
#[cfg(feature = "serde")]
pub fn to_json<T: serde::Serialize>(value: &T) -> String {
serde_json::to_string(value).unwrap()
}
說明:透過
feature,使用者可以自行決定是否要把serde包含在內,減少不必要的依賴,且在發布時仍能維持 SemVer 的相容性。
範例 3:使用 cargo release 發布 patch 版本
# 假設目前版本是 0.3.5
cargo release patch # 產生 0.3.6、建立 tag、推送、發布
執行後會看到類似的輸出:
🔧 Updating version to 0.3.6
🗂️ Creating git tag v0.3.6
🚀 Publishing to crates.io
提示:若想跳過自動發布,只需加上
--no-publish參數。
範例 4:工作區內一次更新所有子 crate 版本
# 在根目錄執行
cargo workspaces version patch # 需要安裝 cargo-workspaces
此指令會:
- 把所有子 crate 的
version從0.2.3→0.2.4 - 更新相互依賴的版本號(如
core = { version = "0.2.4" }) - 建立統一的 Git commit 與 tag
範例 5:在 CI 中自動發布
# .github/workflows/publish.yml
name: Publish
on:
push:
tags:
- 'v*.*.*' # 只在符合 SemVer 的 tag 推送時執行
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --no-verify
說明:透過 GitHub Actions,當你推送
v1.2.3之類的 tag 時,CI 會自動把套件發布到 crates.io,確保每一次正式版都經過相同的流程。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案 / 最佳實踐 |
|---|---|---|
忘記更新 Cargo.toml 的版本 |
發布的套件與程式碼不一致,使用者會卡在舊版依賴 | 使用 cargo release 或 cargo workspaces version 讓版本升級自動化 |
| 在未乾淨的 Git 工作目錄發布 | 可能把測試或暫存檔一起上傳,造成安全或相容性問題 | 在 CI 中加入 git status --porcelain 檢查,或在本機使用 cargo publish --dry-run |
| 依賴的 crate 沒有正確的版本範圍 | 產生相依衝突,導致使用者編譯失敗 | 使用 ^(caret)或 ~(tilde)語意,並在 Cargo.lock 中測試相容性 |
直接在 Cargo.toml 中寫死 Git commit |
若遠端 repo 被重寫或刪除,依賴會失效 | 盡量使用已發布到 crates.io 的版本;若必須使用 Git,請加上 rev 或 tag,並在 CI 中固定 |
| 發布前忘記檢查 License 與 README | crates.io 可能拒絕上傳,或使用者無法了解套件用途 | 在 Cargo.toml 明確填寫 license、description、repository,並確保根目錄有 README.md |
最佳實踐總結:
- 使用 SemVer,每次變更前先決定是
MAJOR、MINOR還是PATCH。 - 在 Git 中建立對應的 Tag,並推送到遠端。
- 自動化流程:
cargo release+ CI(GitHub Actions / GitLab CI)確保每次發布都是可重現的。 - 維護清晰的依賴範圍,避免過寬或過窄的版本限制。
- 在發佈前執行
cargo publish --dry-run,檢查是否有遺漏檔案或不符合 crates.io 規範的資訊。
實際應用場景
| 場景 | 需求 | 解決方案 |
|---|---|---|
| 公司內部共享工具庫 | 需要快速迭代、保證相容性 | 建立私有 crates.io(或使用 GitHub Packages),搭配 cargo release 管理版本,使用 workspace 統一管理多個子 crate |
| 開源函式庫 | 想讓社群容易使用、追蹤問題 | 在 Cargo.toml 完整填寫 repository、homepage、documentation,使用 GitHub Actions 自動發布,並在 README 中說明升級步驟 |
| CLI 應用程式 | 需要發佈二進位檔,同時提供程式庫 | 使用 cargo install 讓使用者直接安裝,並在 Cargo.toml 中設定 [[bin]];發布時同時提供 crate 與 binary 兩種 artefacts |
| 跨平台嵌入式開發 | 依賴特定 target、需要固定版本 | 在 Cargo.toml 使用 target.'cfg(target_arch = "arm")'.dependencies,並在 Cargo.lock 中鎖定版本;發布前使用 cargo check --target=armv7-unknown-linux-gnueabihf 測試相容性 |
總結
- Cargo 不只是編譯與測試工具,更是 發布 與 版本控制 的核心平台。
- 採用 Semantic Versioning、Git 標籤、以及 自動化工具(如
cargo release、CI)可以讓你的套件在每一次上線時都保持一致、可追蹤且不易出錯。 - 透過 Workspace、Feature 與 條件依賴,你可以在同一個 repository 中管理多個套件,同時提供彈性的功能開關,降低相依衝突的風險。
- 最後,別忘了在發布前 dry‑run、檢查 License、README,以及確保 Git 工作目錄乾淨,這些小細節往往決定了套件是否能順利被社群接受。
掌握了上述概念與實務技巧,你就能在 Rust 生態系統中自信地管理、發布與維護自己的程式庫,讓開發流程更流暢、產品更可靠。祝開發順利! 🚀