哈喽,大家好呀,我是呼噜噜,还在被Rust编译时间折磨?依赖管理让你头疼?今天我们来聊聊Rust中能让你的开发效率飙升的神器——工作区Workspace
在Rust中,crate是一个编译单元,是Rust编译器一次性考虑的最小代码量,crate可以是可执行crate(bin)也可以是库crate(lib)
当我们项目逐渐庞大,难以维护或者cargo打包时间过长时,可以将其拆分为多个更小、更易于管理的组件。工作区Workspace就是一种在同一Cargo.toml 根目录下管理多个 crate的机制
它允许你将多个相关的crate结合起来,协同工作、共享依赖关系,以及更方便地进行管理和构建。这些crate共享同一套依赖缓存、Cargo.lock 文件和target 输出目录。Workspace有点类似Java中的Maven父项目与子模块
使用wordspace的优势

那我们使用wordspace有哪些优势:
- 统一依赖管理,避免版本地狱
- 提升编译速度
- 代码复用、模块化项目
- 共享构建产物(target 目录)
- 支持多二进制或多库统一管理
统一依赖管理,避免版本地狱
- 所有
子crate共享一个**根目录的 **Cargo.lock - 当依赖更新时,不需要每个
crate单独维护版本 - 避免了版本冲突和重复下载编译
比如我们在根Cargo.toml里设置,serde和tokio的依赖:
[workspace.dependencies]
serde = "1.0"
tokio = { version = "1.36", features = ["full"] }然后在任意子crate 的Cargo.toml中,就可以如下直接引用:
[dependencies]
serde = { workspace = true }
tokio = { workspace = true }这样就保证了,所有子crate的serde和tokio的依赖都和根目录一致,后续升级依赖的版本时,只需改一个地方,全项目生效
提升编译速度
cargo管理rust项目确实很方便,但为了换取极高性能和极高安全性的运行时,导致cargo在编译时存在编译的中间产物过大,与编译速度过慢的代价
将一个中大型项目的crate,拆分成多个小crate,cargo可以识别,每次编译时,哪些crate被修改:
- 未修改的子模块会被 缓存;
- 只重新编译受影响的 crate;
- 并行构建多个 crate,提高编译效率。
这样使用workspace可以节省30%~70%的编译时间
代码复用、模块化项目
使用workspace,可以将公共逻辑抽到独立 crate,比如 core 或 utils。然后各业务模块(如 service、cli、web)只需引用这些 crate
这样可以使得项目结构清晰,高内聚,低耦合
我们来看一个例子:
my-app/
├── Cargo.toml # workspace root
├── common/ # 公共逻辑(domain、utils)
│ └── Cargo.toml
├── api/ # Web 接口层(axum、warp等)
│ └── Cargo.toml
└── cli/ # 命令行工具(reqwest、tokio)
└── Cargo.toml项目结构如上,api 依赖 common、cli 依赖 common,但它们相互独立,避免循环依赖
共享构建产物(target 目录)
所有 crate 默认使用 同一个 target目录,这样可以避免重复编译相同依赖,节省磁盘空间和时间
如果你单独编译 api 和 cli,是不会重复构建common依赖的,这样编译速度更快
一次性构建/测试
无论你在 Workspace 的哪个目录(根目录、my_cli 里、my_lib 里都一样),执行下面的命令,都可以一次性依此测试 Workspace 中的所有成员
cargo test --workspace如果你仅执行cargo test,仅会测试在 Workspace 中当前目录的成员。当然如果你在根目录执行,那么也会一次性测试Workspace中的所有成员。cargo build也是类似cargo test
支持多二进制或多库统一管理
一个 workspace 可以包含多个可执行程序,比如同时提供一个 CLI 工具、一个后台服务、一个 Web API等
示例:
workspace/
├─ web/
├─ cli/
└─ lib/按照下面命令就可指定服务运行:
cargo run -p cli
cargo run -p web接下来我们具体实操一下,如何创建和使用工作区
创建项目
先创建一个空项目
mkdir my_rust_workspace
cd my_rust_workspace
cargo init该命令执行完成后,我们会在当前目录下生成一个名为my_rust_workspace的项目,并项目中生成一个Cargo.toml配置文件,我们将这个文件所在的目录称为my_rust_workspace这个项目的根目录
根目录的 Cargo.toml 本身不是一个 crate,它只负责管理其他 crate 成员,所以我们删除根目录下的src文件夹
修改根目录下的cargo.toml:
# [package] 这个字段表明着根目录包含的内容是一个package包,那么这个目录需要符合rust的package结构,包含src/main.rs
# name = "my_rust_workspace"
# version = "0.1.0"
# edition = "2024"
# [dependencies]
[workspace]
# "members" 列表告诉 cargo 在哪里寻找我们的 crate
members = [
"my_lib",
"my_app",
]
#在 [workspace.dependencies] 中定义共享依赖
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.48", features = ["full"] }接着我们来创建2个Crate,在根目录执行如下命令:
- 创建
my_lib(库):
cargo new my_lib --lib- 创建
my_app(可执行文件):
cargo new my_app //等同于cargo new my_app --bin我们cargo build一下,看有没有报错,没有就ok
设置 Crate 间的依赖关系
在项目中一般是my_app 依赖 my_lib
- 我们先编辑
my_lib,在这个crate中src下创建lib.rs:
pub fn greet(name: &str) -> String {
format!("Hello, {}! Welcome to my workspace.", name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(greet("Rust"), "Hello, Rust! Welcome to my workspace.");
}
}- 编辑
my_lib的Cargo.toml:
[package]
name = "my_lib"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { workspace = true } #新增这里假设我们的库需要 serde,我们可以直接使用workspace = true 来“继承”根 Cargo.toml 中定义的 serde 版本
- 编辑
my_app的Cargo.toml:
[package]
name = "my_app"
version = "0.1.0"
edition = "2024"
[dependencies]
my_lib = { path = "../my_lib" }
tokio = { workspace = true }注意my_lib = { path = "../my_lib" }这样设置的话,my_app就会依赖my_lib这个crate
- 在
my_app中调用my_lib中的函数
修改my_app中的main.rs:
use my_lib::greet;
fn main() {
let message = greet("Rust Developer");
println!("{}", message);
}- 测试
在根目录下执行一下命令:
#cargo test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src\lib.rs (target\debug\deps\my_lib-25fa19cda2c6dd82.exe)
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests my_lib
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s#cargo run
Hello, Rust Developer! Welcome to my workspace.整个项目测试通过.
可以嵌套Workspace吗?
不可以。Cargo明确不支持嵌套的 Workspace。如果一个 Cargo.toml 定义了 [workspace],那么它的子目录中就不能再有另一个 Cargo.toml 也定义 [workspace]
尾语
如果你的Rust项目正在变得臃肿,编译时间越来越长,现在是时候尝试Workspace了! 它会让你的Rust开发体验提升一个档次。
觉得有用的话,欢迎关注我的公众号,后续会带来更多Rust实战技巧!
本篇文章的完整版demo,扫码到公众号中,回复关键字:my_rust_workspace,即可获取下载链接
作者:小牛呼噜噜
本文到这里就结束啦,感谢阅读,关注同名公众号:小牛呼噜噜,防失联+获取更多技术干货