本文 AI 產出,尚未審核

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 會自動:

  1. 更新 Cargo.toml 中的版本號
  2. 建立 Git commit 與 tag(如 v1.2.4)
  3. 推送至遠端
  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

此指令會:

  1. 把所有子 crate 的 version 從 0.2.3 → 0.2.4
  2. 更新相互依賴的版本號(如 core = { version = "0.2.4" })
  3. 建立統一的 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

最佳實踐總結:

  1. 使用 SemVer,每次變更前先決定是 MAJOR、MINOR 還是 PATCH。
  2. 在 Git 中建立對應的 Tag,並推送到遠端。
  3. 自動化流程:cargo release + CI(GitHub Actions / GitLab CI)確保每次發布都是可重現的。
  4. 維護清晰的依賴範圍,避免過寬或過窄的版本限制。
  5. 在發佈前執行 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 生態系統中自信地管理、發布與維護自己的程式庫,讓開發流程更流暢、產品更可靠。祝開發順利! 🚀