圖:“Rust 的吉祥物 Ferris the Crab 拉著裝滿貨物的貨船”,gemini-2.5-flash-preview,2025年09月16日。
程式邏輯與功能定會逐漸成長,我們當然也知道邏輯要分而治之,昨日的模組化實作就著重在一個大木箱中進行拆分,以 mod.rs
的方式或直接以大木箱中唯一的 lib.rs
當中的模組宣告進行管理。但專案變大避不開的便是所有開發工作的隱藏開銷都會變大,比方說:依賴管理、各種測試、長期功能開發、緊急修正等等,把所有事情都放在同一個箱子,意味每次一編譯都是這個箱子內的事情,那被 Rust 編譯器教訓的概率也挺大的。除非,很自律地執行版本管理的樹,我一個人都好懶惰了,更何況是一群人一起開發,更x3何況如今工程師什麼都要做,什麼都有可能做到一半的情況下,我們很需要一個固定不變的架構,就像我房間永遠對三層櫃不嫌多。
cargo
:我來解救你!
在 cargo
的架構管理中,骨架是可以如此簡單的 -- 模組 module
、大木箱 crate
與工作區 workspace
。對專案配置文件做小更動,就讓你的專案從 crate
晉升成為 workspace
,工作區就是承載各種大木箱的平面。工作區不接受無限套娃(工作區中有工作區),我只面對一層的 crate
大木箱,大家在同一平面上一起做事情,我只管你身為大木箱自帶的 lib.rs
,至於你裡面的模組小紙箱,你就自己整理吧。
這樣的骨架設計既提供了簡單明瞭的平面,讓我們很清楚看到資訊的流動,也給出制式化的彈性,比方說:下個版本我要對某木箱內邏輯進行更改,我大可不必太多動作來保護工程師的第一原則 “程式能動起來,就不要去動了”,自行產生新的箱子,如:crate-a-dev
,繼續做開發和測試,對專案來說就在配置文件中添加或註解即可。
cargo_tutorial/
├── Cargo.toml # 工作區配置
├── src/ # 主應用程式
│ ├── lib.rs # 程式庫根檔案
│ ├── main.rs # 應用程式入口(執行檔入口)
│ └── test_data_generator.rs # 獨立邏輯實作,亦可以視為模組
├── crates/ # crates 目錄(建議以這樣的方式管理)
│ └── csv-converter/ # 獨立的 crate
│ ├── Cargo.toml # crate 專用配置
│ └── src/ # crate 程式庫
│ ├── lib.rs # crate 程式庫根
│ └── converter.rs # 實作邏輯
└── README.md # 記得!寫文件!XD
# 工作區根 Cargo.toml
# 添加 crate,這樣編譯的時候才會看到
[workspace]
members = [
"."
"crates/csv-converter"
]
# 共享依賴配置
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
csv = "1.3"
anyhow = "1.0"
chrono = { version = "0.4", features = ["serde"] }
# src 自身的依賴要找誰
[dependencies]
csv-converter = { path = "./crates/csv-converter" }
csv.workspace = true
anyhow.workspace = true
chrono.workspace = true
crate
Cargo.toml 專用配置文件# crate 內部的 Cargo.toml
# crate 內部 src 自身的依賴要找誰
[dependencies]
serde.workspace = true
serde_json.workspace = true
csv.workspace = true
anyhow.workspace = true
用一張示意圖簡單解釋:serde
、serde_json
、csv
、anyhow
、chrono
是外部的依賴 crate
,我在 workspace 的這個平面 [workspace.dependencies]
來定義了它們的版本。先從 csv-converter
這個 crate
來看,配置文件中都寫上xxx.workspace = true
是意味著這個木箱要使用來自工作區已經定義好的外部依賴,這樣就做到了整個工作區對於外部的依賴是有單一的版本管理的。回到在專案中的 src/
程式庫來說,就看專案的配置文件中的 [dependencies]
,一樣表示了工作區已經一定好的版本,外加我有使用到某個我自行開發的木箱當作我的依賴之一。於是,在 [workspace]
中的 member = [...]
就是大家都放在同一個工作區平面上來進行編譯產出可執行檔案。
workspace
與各大木箱 crate
配置文件撰寫原則[workspace]
members = [
".", # 根 crate (src/)
"crates/csv-converter", # 指定導入某 crate
"crates/*", # 導入 crates 資料夾中所有 crate
"crates/dev/crate-dev" # 路徑寫對亦可
]
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
csv = "1.3"
# 透過路徑的方式亦可以讓 crate 之間相互依賴使用
[dependencies]
csv-converter = { path = "../crates/csv-converter" }
csv = "1.2" # 也可以不使用 workspace 所提供的版本,自行導入外部依賴
鐵人賽開賽到今天已經大致把 cargo
的骨架給描述了,接下來到完賽(打氣!我會努力!XD)間,將會以這種 workspace
的方式,讓每一天的範例成為一個 crate
,來把我們的 Rust 工具箱給裝滿吧!接下來的範例都會放在以下的 Github repo中。盡情期待!
https://github.com/liren0907/rust_one