The Cargo.toml Builder generates the manifest at the heart of every Rust project. Choose whether you are building a binary or a library, pick the crates you need, and the tool emits a complete [package], [dependencies], [dev-dependencies] and release profile.
How it works
[package] holds the crate name, version, edition, authors and license. The target section is either [[bin]] (an executable from src/main.rs) or [lib] (a reusable library from src/lib.rs). [dependencies] lists each crate you selected, using Cargo’s table syntax when features are needed — for example serde is emitted as serde = { version = "1.0", features = ["derive"] } so the derive macros are enabled.
Cargo treats a bare version string as a caret requirement: "1.0" resolves to any 1.x release at or above 1.0.0. The exact versions Cargo actually selects are written to Cargo.lock on first build, which is what makes builds reproducible.
What each crate does
Understanding why a dependency belongs in your project saves you from over-fetching. Here is a brief description of each offered crate:
| Crate | Purpose |
|---|---|
| serde | Serialization and deserialization framework; with the derive feature it generates Serialize/Deserialize impls automatically |
| tokio | Async runtime; the full feature enables timers, I/O, net, process, and the #[tokio::main] macro |
| reqwest | HTTP client built on tokio; handles TLS, redirects, JSON, and multipart |
| axum | Lightweight, type-safe HTTP router and server from the tokio project |
| clap | Derives a CLI from a struct; the derive feature lets you annotate fields for flags, arguments, and subcommands |
| anyhow | Ergonomic error handling; anyhow::Result wraps any error with context without requiring custom error types |
| tracing | Structured, levelled logging compatible with async; pair with tracing-subscriber for output formatting |
Example
After saving the manifest, build to lock dependencies:
cargo build # resolves versions, writes Cargo.lock
cargo build --release # optimized build using [profile.release]
A minimal async binary manifest looks like this:
[package]
name = "myapp"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "myapp"
path = "src/main.rs"
[dependencies]
tokio = { version = "1", features = ["full"] }
anyhow = "1"
[profile.release]
lto = true
codegen-units = 1
Tips and common mistakes
Keep test crates in dev-dependencies. Crates like criterion and tokio-test only belong there; putting them in [dependencies] means every downstream user of your library compiles them unnecessarily.
Commit Cargo.lock for binaries, not libraries. Locking exact versions in a binary ensures reproducible builds across machines. For libraries, leaving it out lets downstream projects pick the newest compatible versions across their own dependency tree.
Features cost compile time. tokio = { version = "1", features = ["full"] } is convenient but pulls in everything. In production, prefer listing only the features you actually use — rt-multi-thread, net, time — to shorten build times.
The release profile is opt-in. lto = true and codegen-units = 1 produce faster, smaller executables but slow down the release compile significantly. They do not affect cargo build (debug) at all, so leave them enabled for final artefacts.