| name | rust-impl-cargo-project |
| description | Use when the user sets up a new Cargo crate, edits Cargo.toml, adds dependencies, configures features (additive principle), tunes profiles, uses `[patch]` / `[replace]`, declares lints in `[lints]`, or wants the MSRV-aware resolver behavior (1.84). Prevents non-additive feature design, mixing dev with build dependencies, accidental release-profile overrides, and missing `rust-version` declarations. Covers: `cargo new` vs `cargo init` (lib / bin), Cargo.toml structure ([package], [dependencies], [dev-dependencies], [build-dependencies], target-specific deps), version requirements (caret / tilde / exact / wildcards), features (additive principle, `dep:` prefix, weak `?` deps, default features), profiles (dev / release / test / bench / custom), `[patch]` and `[replace]`, `[lints]` table (recent stabilization), MSRV-aware resolver (1.84), `package` metadata fields. Keywords: cargo, "Cargo.toml", "cargo new", "cargo init", "cargo add", dependencies, features, "default features", "no-default-features", "dep:" prefix, "weak feature", "?", profile, "release profile", "dev profile", "opt-level", LTO, "codegen-units", patch, replace, "[lints]", "rust-version", MSRV, "MSRV-aware resolver", "resolver 3", "version requirement", "caret", "tilde", "additive features", "feature unification".
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires Rust 1.85+, edition 2024. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
rust-impl-cargo-project
The single-crate Cargo project setup skill: how to scaffold a crate, structure its Cargo.toml, declare dependencies safely, design features that obey the additive principle, tune profiles, override third-party code with [patch] / [replace], configure package-level lints via the [lints] table, and opt into the MSRV-aware resolver introduced in Rust 1.84.
Cross-references: [[rust-impl-workspaces]] (multi-crate layout with [workspace]), [[rust-core-toolchain]] (rustup channels, components, rust-toolchain.toml), [[rust-core-language-versions]] (which features stabilized in which release), [[rust-impl-build-scripts]] (build.rs, OUT_DIR, cargo:: directives).
When to use this skill
- User runs
cargo new or cargo init and asks lib vs bin, what edition, where files go.
- User edits
Cargo.toml and asks about [package], [dependencies], [dev-dependencies], [build-dependencies], or target-specific dependencies.
- User asks about version requirements: caret vs tilde vs exact vs wildcard.
- User designs a feature flag, hits a non-additive design, or asks about
dep: / ? syntax.
- User tunes
[profile.release], defines a custom profile, or asks about LTO, opt-level, codegen-units, strip, panic.
- User wants to override a transitive dependency via
[patch.crates-io] or [replace].
- User wants project-wide lint configuration via the
[lints] table.
- User wants
cargo add to pick versions compatible with their MSRV (Rust 1.84+ resolver).
For multi-crate workspaces ([workspace], shared dependencies, default-members) see [[rust-impl-workspaces]]. For build.rs and the cargo:: directives see [[rust-impl-build-scripts]].
Decision tree: cargo new vs cargo init
Need a fresh directory?
YES -> cargo new <name> (creates ./<name>/)
NO -> cargo init [<path>] (initialises current dir or path)
Binary or library?
Binary (executable, main.rs) -> default, or `--bin` explicitly
Library (lib.rs, no main) -> `--lib`
Both (mixed lib + bin in one crate) -> create lib, then add src/main.rs
Need a different VCS than git?
git (default) | hg | pijul | fossil | none -> `--vcs <name>`
ALWAYS pass --lib when the crate is library-first. NEVER hand-edit Cargo.toml to switch crate type after cargo new; rerun with the right flag in a clean directory or add a [lib] / [[bin]] target explicitly.
cargo new in Rust 1.85+ writes edition = "2024" and uses resolver 3 by default. ALWAYS check the generated edition string when scaffolding inside an existing repo that pinned an older toolchain.
Cargo.toml top-level structure
[package]
[lib] / [[bin]]
[features]
[dependencies]
[dev-dependencies]
[build-dependencies]
[target.<triple>.dependencies]
[profile.<name>]
[patch]
[replace]
[lints]
[workspace]
| Section | Visibility downstream | Triggers rebuild of |
|---|
[dependencies] | yes, exported through the crate | the crate + all consumers |
[dev-dependencies] | NO | tests / examples / benches only |
[build-dependencies] | NO | build.rs |
[target.cfg(unix).dependencies] | yes, but only when cfg matches | the crate on matching targets |
ALWAYS keep proptest, criterion, tokio-test, tempfile in [dev-dependencies]. NEVER put them in [dependencies]: they bloat downstream builds and pollute the public dependency graph.
ALWAYS keep bindgen, cc, pkg-config, cmake in [build-dependencies]. NEVER duplicate them in [dependencies] "just in case": that doubles compile time.
[package] metadata: required + strongly recommended fields
[package]
name = "my-crate"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
license = "MIT OR Apache-2.0"
description = "What this crate does, one paragraph."
repository = "https://github.com/org/repo"
keywords = ["cli", "parser"]
categories = ["command-line-utilities"]
readme = "README.md"
Source: Cargo manifest reference
ALWAYS declare rust-version once the crate is published or shared. Without it the MSRV-aware resolver cannot help downstream users.
NEVER omit description and license on a crate destined for crates.io: publication will fail.
ALWAYS prefer the SPDX expression syntax ("MIT OR Apache-2.0") over license-file unless the license has no SPDX identifier.
Version requirements (semver)
| Syntax | Meaning | Compatible range | Use when |
|---|
"1.2.3" | caret, implicit | >=1.2.3, <2.0.0 | default; allows minor + patch updates |
"^1.2.3" | caret, explicit | >=1.2.3, <2.0.0 | same as above, spelled out |
"~1.2.3" | tilde | >=1.2.3, <1.3.0 | pin minor, allow patch only |
"=1.2.3" | exact | =1.2.3 only | reproducing a bug; library code should AVOID |
"1.2" | caret on shorter version | >=1.2.0, <2.0.0 | allow any patch + future minors |
"1" | caret on major | >=1.0.0, <2.0.0 | broadest stable range |
"*" | wildcard | any version | NEVER in published crates |
">=1.2, <1.5" | multiple | >=1.2.0, <1.5.0 | excluding a known-bad range |
ALWAYS use bare caret ("1.2.3") in library crates so consumers can dedupe transitive versions.
NEVER use "=x.y.z" in a published library: it forces every downstream graph to that exact version and breaks dedup.
NEVER use "*" outside throwaway scripts: it implies "any future major version is fine", which is never true.
Features: the additive principle
"A consequence of this is that features should be additive. That is, enabling a feature should not disable functionality, and it should usually be safe to enable any combination of features."
Source: Cargo features reference
This single rule drives every other feature-design decision.
[features]
default = ["std"]
std = []
serde = ["dep:serde"]
async = ["dep:tokio", "serde?/derive"]
full = ["std", "serde", "async"]
| Syntax | Effect | Stabilised |
|---|
feature = [] | a bare feature flag, gates #[cfg(feature = "...")] | always |
feature = ["other"] | enabling feature also enables other | always |
optional = true on a dep | implicitly creates a feature of the same name | always |
feature = ["dep:foo"] | enables optional dep foo WITHOUT creating an implicit feature | 1.60 |
feature = ["foo?/bar"] | enables bar feature of foo ONLY if foo is enabled elsewhere | 1.60 |
ALWAYS phrase a flag as "enables X". NEVER phrase it as "disables X" (e.g. an no_std feature is the wrong direction; use an opt-in std feature instead).
NEVER design two features that must not be enabled together. Feature unification means any consumer in the dep graph can flip either one on, and Cargo will refuse to fail loudly.
ALWAYS use dep:foo when an optional dependency name should NOT become a public feature flag. Otherwise users can --features foo directly, locking you into that name forever.
Profiles: compile-time knobs
Built-in profiles and their inheritance:
dev --(inherited by)--> test
release --(inherited by)--> bench
Source: Cargo profiles reference
| Setting | dev default | release default | Override scope |
|---|
opt-level | 0 | 3 | per package |
debug | true | false | per package |
debug-assertions | true | false | per package |
overflow-checks | true | false | per package |
lto | false | false | profile-wide only |
panic | "unwind" | "unwind" | profile-wide only |
incremental | true | false | profile-wide only |
codegen-units | 256 | 16 | per package |
strip | "none" | "none" | per package |
LTO modes: false (thin-local LTO on the current crate), "thin", true / "fat" (cross-crate full LTO), "off" (disabled).
Panic strategies: "unwind" (default, supports catch_unwind), "abort" (smaller binaries, no unwinding).
[profile.release]
lto = "thin"
codegen-units = 1
strip = "symbols"
[profile.release-debug]
inherits = "release"
debug = true
[profile.dev.package."*"]
opt-level = 2
ALWAYS set panic = "abort" only AFTER verifying no dependency relies on std::panic::catch_unwind. NEVER set it just to shrink binaries by reflex.
NEVER override panic, lto, or rpath in per-package overrides: only profile-wide settings accept those. Cargo errors out otherwise.
[patch] and [replace]: overriding sources
[patch.crates-io]
serde = { git = "https://github.com/me/serde", branch = "fix-1234" }
[patch.crates-io]
serde = { path = "../serde" }
ALWAYS prefer [patch] over the legacy [replace]. [patch] is workspace-aware and respects semver; [replace] is deprecated.
ALWAYS keep [patch] entries on a feature branch, not on main. NEVER ship a published crate that patches a registry dep: downstream cannot inherit [patch] from a dependency.
[lints] table: package-level lint configuration
[lints.rust]
unsafe_code = "forbid"
unused_imports = "deny"
missing_docs = { level = "warn", priority = -1 }
[lints.clippy]
enum_glob_use = "deny"
pedantic = { level = "warn", priority = -1 }
Levels: forbid, deny, warn, allow. Tool name comes from the part before :: in the lint name (unsafe_code -> lints.rust; clippy::enum_glob_use -> lints.clippy).
"These will only affect local development of the current package. Cargo only applies these to the current package and not to dependencies."
Source: Cargo manifest reference
priority lets a group (e.g. clippy::pedantic at priority = -1) be overridden by individual lints (default priority = 0).
ALWAYS use [lints] in Cargo.toml for new projects instead of #![deny(...)] at the crate root: the manifest form is grep-friendly, machine-editable, and inheritable across a workspace via lints.workspace = true (see [[rust-impl-workspaces]]).
NEVER set unsafe_code = "forbid" in a crate that contains unsafe blocks: forbid cannot be overridden by inner attributes, the build will fail.
MSRV-aware resolver (Rust 1.84+)
Three activation paths:
Cargo.toml: [package] resolver = "3" (requires the host toolchain to be >= 1.84).
.cargo/config.toml: [resolver] incompatible-rust-versions = "fallback".
- Edition 2024 default: a fresh
cargo new on Rust 1.85+ scaffolds with resolver 3 already enabled.
Source: Rust 1.84.0 release notes
Effect: when rust-version is set, cargo add and cargo update prefer dependency versions that still build on that MSRV instead of the absolute latest.
[package]
name = "demo"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
resolver = "3"
Override for CI runs that should test against the latest dep versions:
CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=allow cargo update
ALWAYS declare rust-version before opting into resolver 3, otherwise the resolver has no MSRV to honour.
NEVER raise rust-version casually: every bump is a breaking change for downstream pinned at the old MSRV.
Quick recipes
Add a runtime dep with explicit features (no defaults)
cargo add tokio --no-default-features --features rt,macros,sync
Add a dev-only dep
cargo add --dev proptest
Add an optional dep gated behind a feature
cargo add serde --optional --features derive
Patch a transitive dep on a branch
[patch.crates-io]
libc = { git = "https://github.com/rust-lang/libc", branch = "main" }
Test the crate under all feature permutations
cargo install cargo-hack
cargo hack check --feature-powerset --no-dev-deps
Validation checklist
Before committing a Cargo.toml change, verify:
cargo metadata --format-version 1 > /dev/null succeeds (manifest parses).
cargo build and cargo build --release both succeed.
cargo build --no-default-features succeeds when the crate claims to support that.
cargo build --all-features succeeds (features are truly additive).
rust-version is set when the crate is shared or published.
- Dev / build / runtime deps are in the correct section.
[lints] block has no forbid on a lint the code violates.
Reference links
- [[references/methods.md]] : full
Cargo.toml key reference (every [package] field, every dependency-table key, every profile setting with type).
- [[references/examples.md]] : complete
Cargo.toml recipes (binary crate, library crate, FFI crate with build.rs, no_std crate, MSRV-pinned crate, custom profile, [patch] override).
- [[references/anti-patterns.md]] : non-additive features, dev-deps in
[dependencies], panic = "abort" without verification, exact-version pinning in libraries, missing rust-version, copying release profile blindly.