本文 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 的 version0.2.30.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 releasecargo workspaces version 讓版本升級自動化
在未乾淨的 Git 工作目錄發布 可能把測試或暫存檔一起上傳,造成安全或相容性問題 在 CI 中加入 git status --porcelain 檢查,或在本機使用 cargo publish --dry-run
依賴的 crate 沒有正確的版本範圍 產生相依衝突,導致使用者編譯失敗 使用 ^(caret)或 ~(tilde)語意,並在 Cargo.lock 中測試相容性
直接在 Cargo.toml 中寫死 Git commit 若遠端 repo 被重寫或刪除,依賴會失效 盡量使用已發布到 crates.io 的版本;若必須使用 Git,請加上 revtag,並在 CI 中固定
發布前忘記檢查 License 與 README crates.io 可能拒絕上傳,或使用者無法了解套件用途 Cargo.toml 明確填寫 licensedescriptionrepository,並確保根目錄有 README.md

最佳實踐總結

  1. 使用 SemVer,每次變更前先決定是 MAJORMINOR 還是 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 完整填寫 repositoryhomepagedocumentation,使用 GitHub Actions 自動發布,並在 README 中說明升級步驟
CLI 應用程式 需要發佈二進位檔,同時提供程式庫 使用 cargo install 讓使用者直接安裝,並在 Cargo.toml 中設定 [[bin]];發布時同時提供 cratebinary 兩種 artefacts
跨平台嵌入式開發 依賴特定 target、需要固定版本 Cargo.toml 使用 target.'cfg(target_arch = "arm")'.dependencies,並在 Cargo.lock 中鎖定版本;發布前使用 cargo check --target=armv7-unknown-linux-gnueabihf 測試相容性

總結

  • Cargo 不只是編譯與測試工具,更是 發布版本控制 的核心平台。
  • 採用 Semantic VersioningGit 標籤、以及 自動化工具(如 cargo release、CI)可以讓你的套件在每一次上線時都保持一致、可追蹤且不易出錯。
  • 透過 WorkspaceFeature條件依賴,你可以在同一個 repository 中管理多個套件,同時提供彈性的功能開關,降低相依衝突的風險。
  • 最後,別忘了在發布前 dry‑run、檢查 LicenseREADME,以及確保 Git 工作目錄乾淨,這些小細節往往決定了套件是否能順利被社群接受。

掌握了上述概念與實務技巧,你就能在 Rust 生態系統中自信地管理、發布與維護自己的程式庫,讓開發流程更流暢、產品更可靠。祝開發順利! 🚀