What Makes Cargo Different

Cargo is Rust's official build system and package manager, and it's widely regarded as one of the best-designed dependency management tools in any language ecosystem. Unlike npm or pip, Cargo was designed alongside the language itself, which means dependency management is a first-class concern rather than an afterthought.

Understanding how Cargo works under the hood helps you write better Rust projects, debug tricky build errors, and reason about your project's security posture.

The Core Files: Cargo.toml and Cargo.lock

Every Cargo project has two key files:

  • Cargo.toml — The manifest. You declare your dependencies here with version requirements (e.g., serde = "1.0"). This is human-edited.
  • Cargo.lock — The lock file. Cargo generates and updates this automatically. It records the exact resolved version of every dependency (direct and transitive). This is machine-managed.

For applications (binaries), you should commit Cargo.lock to version control. For libraries, the convention is to not commit it, so downstream consumers can resolve the latest compatible versions themselves.

How Cargo Resolves Versions

Cargo uses Semantic Versioning (SemVer) as its contract system. When you write serde = "1.0", Cargo interprets this as "any version ≥ 1.0.0 and < 2.0.0". This is called a caret requirement and is Cargo's default.

Cargo's resolver (version 2 as of Rust 2021 edition) works to find a single set of package versions that satisfies all constraints across your entire dependency tree. Crucially, unlike npm, Cargo does not allow multiple major versions of the same crate to coexist by default — it tries to unify them to a single version.

The crates.io Registry

crates.io is the central registry for the Rust ecosystem, analogous to npm's registry or PyPI. Key characteristics:

  • Packages (called crates) are versioned and immutable once published.
  • Crate owners can yank a version (marking it as broken), but the source is never deleted — preserving reproducibility.
  • Cargo downloads crates from crates.io and caches them locally in ~/.cargo/registry.

Features: Conditional Dependencies

One of Cargo's most powerful concepts is features — optional functionality in a crate that can be toggled on or off. A crate might offer a serde feature that only pulls in serde as a dependency when enabled. This keeps compile times and binary sizes smaller for consumers who don't need every capability.

[dependencies]
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }

Inspecting the Dependency Tree

Cargo ships with a built-in subcommand for exploring your dependency tree:

cargo tree

This prints the full dependency graph in your terminal. Useful flags include:

  • --duplicates — Shows crates that appear more than once at different versions.
  • --invert <crate> — Shows which packages depend on a given crate (great for auditing).
  • --features <feature> — Shows the tree when a specific feature is enabled.

Security Auditing with cargo-audit

The cargo-audit tool checks your Cargo.lock against the RustSec Advisory Database — a community-maintained list of known vulnerabilities in Rust crates. Install and run it with:

cargo install cargo-audit
cargo audit

This is one of the fastest ways to catch known CVEs in your dependency tree before they become a problem in production.

Summary

Cargo's thoughtful design — with its strict SemVer enforcement, first-class lock files, and composable feature system — makes Rust one of the most reproducible and auditable language ecosystems available. Understanding these fundamentals helps you leverage Cargo's full power and keep your Rust projects healthy and secure.