Cargo.toml Explained
Cargo.toml is the manifest Cargo reads for every build. It declares your crate identity, dependencies, features, profiles, and workspace membership.
Recipe
[package]
name = "api"
version = "0.1.0"
edition = "2024"
rust-version = "1.97"
[dependencies]
axum = "0.8"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
tower = { version = "0.5", features = ["util"] }When to reach for this: Any time you add a crate, pin a Rust version, or tune build profiles.
Working Example
A small Axum service manifest with workspace-ready dependency pins:
[package]
name = "orders-api"
version = "0.3.1"
edition = "2024"
rust-version = "1.97"
license = "MIT OR Apache-2.0"
description = "Order HTTP API"
repository = "https://github.com/acme/orders"
[dependencies]
axum = { version = "0.8", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }
thiserror = "2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tracing = "0.1"
[dev-dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
[profile.release]
lto = "thin"
codegen-units = 1What this demonstrates:
edition = "2024"selects language defaults for the craterust-versiontells Cargo the MSRV; CI should match- Feature flags on dependencies trim unused code paths
[profile.release]tunes the shipping binary
Deep Dive
[package] Metadata
| Field | Purpose |
|---|---|
name | Crate name on crates.io and in use paths |
version | Semver for releases |
edition | 2018, 2021, or 2024 language surface |
rust-version | Minimum supported Rust (MSRV) |
license / license-file | Required for publishing |
description | One-line crates.io summary |
repository | Source URL for docs and cargo package |
Dependency Tables
| Table | When |
|---|---|
[dependencies] | Always linked in normal builds |
[dev-dependencies] | Tests, benches, examples only |
[build-dependencies] | Used by build.rs only |
Version specifiers:
serde = "1.0" # compatible with 1.0.x
tokio = { version = "1", features = ["rt"] }
sqlx = { version = "0.8", default-features = false }
local = { path = "../shared" }
git2 = { git = "https://github.com/org/git2-rs", rev = "abc123" }[features] and [lints]
[features]
default = ["postgres"]
postgres = ["sqlx/postgres"]
[lints.rust]
unsafe_code = "forbid"- Features propagate to optional dependencies
[lints](Rust 1.74+) sets crate-level lint levels in the manifest
Gotchas
- Caret vs exact versions -
"1.0"allows1.9oncargo update. Fix: pin with"=1.0.210"only when you must reproduce an exact build. - Missing
rust-version- contributors on older toolchains get confusing errors. Fix: set MSRV and document in README. - Duplicate crate names - renaming a package without updating
usepaths breaks dependents. Fix: treat renames as semver-major for libraries. - Feature explosion - every optional dep should map to a named feature. Fix: avoid
optional = truewithout a feature flag consumers can enable. - Workspace deps not inherited - members repeat version pins. Fix: use
[workspace.dependencies]andworkspace = truein members.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
cargo add CLI | Quick edits with correct syntax | Bulk scripted manifest generation |
[patch] / [replace] | Local forks during development | Permanent dependency strategy |
Cargo.lock only | Binary apps need reproducibility | Published libraries (usually omit lock) |
FAQs
What is edition 2024?
Edition 2024 enables newer language defaults (e.g. gen blocks, updated prelude). It is orthogonal to the Rust compiler version; you still need Rust 1.97+ for the toolchain features you use.
Should libraries commit Cargo.lock?
Generally no for libraries on crates.io; yes for applications and workspace roots so CI and deploys are reproducible.
How do I pin a git dependency?
Use git, branch, tag, or rev keys. Prefer tagged releases over floating branches for production.
What is default-features = false?
Disables the dependency's default feature set so you opt in explicitly, reducing compile time and binary size.
Can one repo have multiple packages?
Yes via [workspace] with multiple members. Each member has its own [package] section.
How do I share versions across workspace members?
Define versions once under [workspace.dependencies] and reference with dep = { workspace = true }.
What does publish = false do?
Marks a workspace member as internal-only; cargo publish skips it even if version bumps occur.
How do I exclude files from the published crate?
Use exclude = [...] or include = [...] under [package]; cargo package --list shows the tarball contents.
Where do binary-specific settings go?
[[bin]] tables name extra binaries; [[example]] and [[test]] customize targets beyond defaults.
How does MSRV interact with dependencies?
If a dependency requires Rust 1.80 and you set rust-version = "1.70", cargo build fails with a clear MSRV error. Raise your floor or downgrade the dependency.
Related
- Cargo Basics - project layout and commands
- Features & Optional Dependencies - conditional compilation
- Dependency Management - updates and auditing
- Workspaces - shared manifests
- Profiles & Build Settings - release tuning
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.