Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Cargo, Workspaces, Features, and Dependencies

Rule

Keep Cargo configuration explicit: share policy through workspaces, add dependencies only for identified needs, and keep reusable-library features additive and minimal.

Why

Cargo choices affect compilation, public API, binary size, MSRV, and downstream compatibility long after the immediate change.

Do

  • Use a workspace when multiple crates share package, dependency, lint, or profile policy.
  • Put shared dependency versions in [workspace.dependencies] only after a member needs them.
  • Add mature domain crates when they remove real complexity or improve correctness.
  • Be conservative with library dependencies and pragmatic with application dependencies.
  • Prefer clap derive for nontrivial application CLIs; hand-parse only tiny private interfaces.
  • Keep reusable-library features additive and opt-in.
  • Make serialization optional unless it is core to the crate, and treat published formats as contracts.
  • Verify changed reusable libraries with all supported features and the declared MSRV.

Avoid

  • Do not prepopulate a new project with optional ecosystem dependencies.
  • Do not add a crate for a trivial wrapper around std.
  • Do not expose dependency types publicly unless they are an intended contract.
  • Do not add mutually exclusive or speculative feature flags.
  • Do not make default library features pull in heavy optional integrations.
  • Do not derive public serialization without deciding compatibility behavior.

Library vs Application

Libraries minimize defaults and public dependency exposure. Applications depend directly on concrete integrations and normally do not feature-gate internal implementation details.

Example

[dependencies]
serde = { version = "1", features = ["derive"], optional = true }

[features]
serde = ["dep:serde"]

The feature exists because serialization is a real optional integration, not because the crate might need it later.

Exceptions

  • Use a heavier dependency when it is the mature ecosystem standard for the domain.
  • Use default features for an intentionally batteries-included library.
  • Pin exact versions when reproducibility, upstream breakage, or security response requires it.
  • Document a feature matrix when unavoidable integrations cannot be enabled together.