Cargo 工作区实践:多包项目先把边界切清楚 Cargo 工作区实践多包项目先把边界切清楚一、工作区不是为了显得项目很大Rust 项目从单个 crate 变成多个 crate 时很容易想到 Cargo workspace。但工作区不是为了让目录看起来专业而是为了管理多个边界清晰的包。比如一个 AI CLI 项目可以拆成命令行入口、核心逻辑、插件协议和测试工具。拆分的目的是让依赖方向更清楚。如果模块之间还没有稳定边界过早拆成多个 crate 反而会增加复杂度。每次改一个类型都要调整多个包的依赖和可见性。我的经验是先在单包里写清模块等某个模块确实可以独立复用或需要隔离依赖时再拆成 crate。我自己踩过一次坑。一个 AI CLI 项目拆成了 agent-core、agent-cli、plugin-sdk 三个 crate。拆完之后每次改核心数据结构三个 crate 要分别改一遍。更难受的是plugin-sdk 引用了 agent-core 的一个内部类型一旦 agent-core 重构plugin-sdk 就编译不过。拆得太早边界还没稳定反而增加了很多心智负担。二、依赖关系入口依赖核心核心不要依赖入口flowchart TD A[cli crate] -- B[core crate] A -- C[plugin-runtime crate] C -- B D[xtask crate] -- A B -- E[domain types]工作区里最重要的是依赖方向。CLI 入口可以依赖 core因为它要调用业务逻辑core 不应该依赖 CLI否则核心逻辑就被终端交互污染。插件运行时可以依赖协议类型但协议类型最好保持轻量避免引入运行时依赖。测试工具和脚本可以放进xtask。这样构建、生成代码、检查文档等任务可以用 Rust 写而不是散落在 shell 脚本里。对初学者来说xtask 也是练习 Rust 工程化的好地方。三、配置示例最小 workspace下面是一个简单的根目录Cargo.toml。[workspace] members [ crates/agent-cli, crates/agent-core, crates/plugin-runtime, xtask ] resolver 2 [workspace.package] edition 2021 license MITresolver 2可以让依赖特性解析更合理尤其是工作区里有多个包时。共享版本信息也可以放在[workspace.package]或[workspace.dependencies]中减少重复配置。不要每个 crate 自己写一套版本后面升级会很烦。每个 crate 的公开 API 要克制。能pub(crate)就不要pub能通过函数暴露就不要直接公开内部结构。工作区不是把所有模块摊开而是让边界更明确。Rust 的可见性控制很适合做这件事。生产环境实战经验工作区里最容易出现的问题是循环依赖。有一次 agent-core 为了打日志引入了 agent-cli 的 Loggeragent-cli 当然也依赖 agent-core。Cargo 直接报错但因为 workspace 里信息分散我一开始没注意到方向反了。后来总结的规则很简单core 不依赖 cli不依赖 pluginplugin 依赖 core 但不依赖 clicli 可以依赖一切。依赖方向写在 README 里新成员一眼能看懂。四、工程习惯检查命令统一起来工作区项目应有统一检查命令例如cargo fmt --all、cargo clippy --workspace --all-targets、cargo test --workspace。这些命令可以写进 README 或 xtask。这样每次提交前知道该跑什么CI 也能复用。版本发布也要考虑。多个 crate 是否一起发版还是独立发版内部 crate 是否需要发布到 crates.io如果只是项目内部使用可以设置publish false。这些小配置能避免误发布。最后拆分后要关注编译时间。crate 太多、泛型太重、特性开关复杂都会让构建变慢。工程化不是无限拆包而是在清晰边界和维护成本之间取平衡。能简单就先简单。编译时间数据拆分前后的对比我做过一次对比。一个项目单包编译clean build 大约 45 秒。拆成 5 个 crate 后增加到 68 秒因为每个 crate 都要独立做类型检查和单态化。但增量编译时拆分后的收益就出来了只改 cli 时等待时间从 12 秒降到了 4 秒。要不要拆取决于你多久改一次核心 crate。如果每天都在大改 core先别拆。五、总结Cargo workspace 适合管理边界清晰的多包 Rust 项目。入口依赖核心核心不依赖入口共享配置放根目录公开 API 保持克制统一 fmt、clippy 和 test 命令。工作区的价值不在目录多而在依赖关系清楚。