本文 AI 產出,尚未審核

Cargo 專案結構解析

簡介

在 Rust 生態系統中,Cargo 不只是套件管理工具,更是整個開發流程的核心。它負責建置、測試、發佈以及依賴管理,讓開發者可以把注意力放在程式本身的邏輯上。
了解 Cargo 產生的專案目錄與檔案結構,對於新手快速上手、以及中階開發者維護大型程式碼庫都相當重要。正確的結構不僅提升可讀性,也能減少編譯錯誤與依賴衝突的機會。

本篇文章將從 Cargo 初始化 的結果說起,逐層剖析每個目錄與檔案的意義,並提供實作範例、常見陷阱與最佳實踐,最後說明在真實專案中如何運用這些概念。


核心概念

1. Cargo.toml – 專案的「說明書」

Cargo.toml 使用 TOML 語法描述專案的基本資訊、依賴、編譯設定等。最小範例:

[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
  • nameversionedition 為必填欄位。
  • [dependencies] 區塊列出所有外部 Crate,支援版本範圍、功能旗標 (features) 等進階設定。

小技巧:在多套件 (workspace) 中,Cargo.toml 也可以包含 [workspace] 區塊,統一管理子專案的相依性。


2. src/ 目錄 – 程式碼的主體

檔案/目錄 說明
main.rs 二進位 (binary) 專案的入口點。執行 cargo run 時會編譯此檔案。
lib.rs 函式庫 (library) 專案的根模組。其他 crate 會以 use crate_name::... 引入。
bin/ 可放置多個二進位檔,每個檔案會產生獨立的可執行檔。
tests/ 整合測試 (integration tests) 的目錄,檔名會被視為獨立測試模組。
examples/ 範例程式碼,cargo run --example <name> 可直接執行。

範例 1:簡易 main.rs

// src/main.rs
fn main() {
    // 印出 Hello, Cargo!
    println!("Hello, Cargo!");
}

範例 2:建立可重複使用的函式庫

// src/lib.rs
/// 計算兩個整數的最大公因數 (GCD)
pub fn gcd(mut a: u64, mut b: u64) -> u64 {
    while b != 0 {
        let r = a % b;
        a = b;
        b = r;
    }
    a
}

// 讓測試模組能存取 `gcd`
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gcd() {
        assert_eq!(gcd(48, 18), 6);
    }
}

範例 3:多執行檔 (bin/)

my_project/
├─ src/
│  └─ main.rs
└─ src/
   └─ bin/
      ├─ server.rs
      └─ client.rs
// src/bin/server.rs
fn main() {
    println!("啟動伺服器…");
    // 這裡放置網路監聽程式碼
}
// src/bin/client.rs
fn main() {
    println!("執行客戶端…");
    // 這裡放置連線到伺服器的程式碼
}

執行方式:

cargo run --bin server   # 執行 server.rs
cargo run --bin client   # 執行 client.rs

3. target/ 目錄 – 編譯產物的暫存區

cargo buildcargo testcargo run 都會把產出的 .rlib.exe.d 等檔案放在 target/

  • debug/:預設的除錯編譯,速度較快、包含除錯資訊。
  • release/:使用 --release 產生最佳化後的二進位檔,適合部署。

注意target/ 不應 被加入版本控制 (git)。在 .gitignore 中加入 target/ 可以避免不必要的檔案被提交。


4. Cargo.lock – 依賴版本鎖定

當第一次執行 cargo build 時,Cargo 會解析 Cargo.toml 中的相依性,並把解析後的具體版本寫入 Cargo.lock

  • 應該提交:對於應用程式 (binary) 專案,將 Cargo.lock 加入版本控制可以保證所有開發者與 CI 環境使用相同的依賴版本。
  • 不必提交:對於 library crate,通常不需要提交,讓使用者自行決定相依版本。

5. 工作區 (Workspace)

當一個 repo 包含多個相互關聯的 crate 時,使用 Workspace 可以共用 Cargo.lock、統一編譯設定,減少重複建置。

# Cargo.toml (根目錄)
[workspace]
members = [
    "core",
    "cli",
    "api",
]

每個子目錄 (core/, cli/, api/) 都會有自己的 Cargo.toml,但編譯時會共享同一個 target/


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記 .gitignore target/Cargo.lock(對 library)會被誤加入 Git,導致 repo 膨脹。 在專案根目錄放置標準的 .gitignore,內容可參考 https://github.com/github/gitignore/blob/main/Rust.gitignore
依賴版本衝突 多個 crate 依賴不同版本的同一套件,編譯失敗。 使用 cargo tree 檢視依賴樹,必要時在 [patch.crates-io]cargo update -p 針對特定套件強制升級。
測試檔案放錯位置 把單元測試寫在 tests/,但忘了加 #[cfg(test)],導致編譯失敗。 單元測試放在 src/*.rs 內的 mod tests,整合測試則放在 tests/
忽略 edition 使用舊版語法(如 try!)在新 edition 中會警告。 Cargo.toml 明確指定 edition = "2021",並遵循該版的語法與特性。
過度依賴 unwrap() 在正式二進位檔中直接 unwrap() 會在錯誤時 panic,影響使用者體驗。 使用 Resultanyhowthiserror 等錯誤處理庫,提供友善的錯誤訊息。

最佳實踐

  1. 保持目錄乾淨:只在 src/ 放置程式碼,其他如 scripts/assets/ 放在根目錄或 examples/
  2. 使用工作區:大型專案或微服務架構時,將共用程式碼抽成 library,其他 binary 只負責 I/O。
  3. 定期更新依賴cargo outdated 可以檢測過期套件,cargo audit 會掃描已知安全漏洞。
  4. CI/CD 整合:在 GitHub Actions、GitLab CI 中加入 cargo fmt -- --checkcargo clippy -- -D warnings,確保程式碼風格與 lint 通過。

實際應用場景

1. 建立 CLI 工具

使用 src/main.rs 作為入口,將核心演算法抽成 src/lib.rs,再在 Cargo.toml 加入 clapanyhow 等依賴。透過 cargo install --path . 可以直接把工具安裝到使用者的 $HOME/.cargo/bin

2. 開發 Web 服務

在工作區中建立 api/ (Rocket/Actix) 與 core/(資料模型、商業邏輯)兩個 crate。api 只負責路由與請求處理,core 提供純函式庫,讓未來的 CLI 或測試程式也能重複使用。

3. 撰寫範例與教學

examples/ 目錄作為「可執行教學」的容器。每個範例都可以使用 cargo run --example <name> 直接執行,方便新手快速驗證概念。


總結

  • Cargo 不只是建置工具,它定義了 Rust 專案的標準結構,讓程式碼、測試、範例與依賴管理都有一致的規範。
  • 了解 Cargo.tomlsrc/target/Cargo.lock 以及 Workspace 的角色,可大幅提升開發效率與專案可維護性。
  • 避免常見的目錄與依賴錯誤,並遵守 最佳實踐(如 CI 整合、錯誤處理、定期更新),即可在實務上建立穩定、可擴充的 Rust 應用。

掌握了 Cargo 的專案結構後,你就能更專注於寫出安全、快速且易於維護的 Rust 程式碼,無論是小型腳本、CLI 工具,或是大型微服務,都能在同一套流程中順利完成。祝開發順利,玩得開心!