Here's a rather large workspace with desktop apps, server apps, CLI tools, and libraries:
https://github.com/storytold/artcraft/blob/main/Cargo.toml
Cargo allows you to universally pin dependencies and features across the entire repo (if you'd like), though you can also deviate from this.
Javascript, on the other hand, is not very fun. I've used Nx, Lerna, and native tools. The tooling is esoteric, and the setup is arcane.
epage•13m ago
- splitting up a package into smaller compilation units to speed up builds as llvm doesn't like large compilation units, e.g. I'm assuming this is why uv is split up
- splitting semver concerns (easiest if different parts of the api are versioned independently), e.g. one hope for splitting out `serde_core` is to allow breaking changes of `serde_derive`
- splitting out optional parts to improve build time (`features` can also be used but usability isn't as great), e.g. `clap` (cli parser) and `clap_complete` (completion support)
- local development tools, e.g. https://github.com/matklad/cargo-xtask
- less commonly run tests or benches that have expensive dependencies, e.g. `toml` splits out a benchmark package for comparing performance of `toml`, `toml_edit`, an old version of `toml`, and `serde_json`
- proc-macros generally have a regular library they generate code against
- lower development overhead to develop them together, e.g. anstyle has a lot of related packages and I wouldn't be wanting to maintain a repo per package
Packages also let you mix a library with bins and examples. We talked about multi-purpose packages vs libraries at https://blog.rust-lang.org/inside-rust/2024/02/13/this-devel... (this was before workspace publishing was supported).
Something this doesn't mention is that Cargo workspaces allow for sharing parts of the manifest through "workspace inheritance", including package metadata like the repo, dependency version requirements, lint configuration, or compilation profiles.