Skill

cargo-conventions

This skill should be used when managing Cargo.toml configuration, workspace setup, dependency management, feature flags, publishing crates, or configuring build profiles in Rust projects.

From ccfg-rust
Install
1
Run in your terminal
$
npx claudepluginhub jsamuelsen11/claude-config --plugin ccfg-rust
Tool Access

This skill uses the workspace's default tool permissions.

Skill Content

Cargo Conventions and Configuration Patterns

This skill defines comprehensive conventions for managing Rust projects with Cargo, covering Cargo.toml structure, workspace management, dependency practices, feature flags, build profiles, lints, and publishing.

Cargo.toml Structure

Section Ordering

Cargo.toml sections should follow a consistent order for readability.

# CORRECT: Standard section ordering
[package]
name = "my-crate"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
description = "A brief description"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/my-crate"
readme = "README.md"
keywords = ["keyword1", "keyword2"]
categories = ["category"]

[features]
default = ["json"]
json = ["dep:serde_json"]
xml = ["dep:quick-xml"]

[dependencies]
serde = { version = "1", features = ["derive"] }
thiserror = "2"

[dev-dependencies]
proptest = "1"
tokio = { version = "1", features = ["test-util", "macros"] }

[build-dependencies]
prost-build = "0.13"

[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }

[profile.release]
lto = true
# WRONG: Randomly ordered sections
[dev-dependencies]
proptest = "1"

[profile.release]
lto = true

[package]
name = "my-crate"
version = "0.1.0"

[dependencies]
serde = "1"

Edition and MSRV

Always specify the edition. Set rust-version (MSRV) for libraries to communicate the minimum supported Rust version.

# CORRECT: Edition and MSRV specified
[package]
name = "my-lib"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
# WRONG: Missing edition (defaults may change between Cargo versions)
[package]
name = "my-lib"
version = "0.1.0"

Lint Configuration

Configure Lints in Cargo.toml

Since Rust 1.74, clippy lints should be configured in Cargo.toml using the [lints] table instead of #![warn(...)] attributes in source files.

# CORRECT: Lints in Cargo.toml (modern approach)
[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
unwrap_used = "warn"
expect_used = "warn"

[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
# WRONG: Using a separate clippy.toml for lint configuration
# clippy.toml files are for clippy-specific settings like msrv,
# not for lint levels
// WRONG: Lint attributes in source files (use Cargo.toml instead)
#![warn(clippy::all, clippy::pedantic)]
#![forbid(unsafe_code)]

Workspace Lint Inheritance

In workspaces, define lints once at the workspace level and inherit them in member crates.

# CORRECT: Workspace root Cargo.toml
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
unwrap_used = "warn"

# Member crate Cargo.toml
[lints]
workspace = true
# WRONG: Duplicating lint config in every member crate
# crate-a/Cargo.toml
[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }

# crate-b/Cargo.toml (same config repeated)
[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }

Workspace Management

Workspace Structure

Use workspaces for multi-crate projects to share dependencies and build artifacts.

# CORRECT: Workspace root Cargo.toml
[workspace]
resolver = "2"
members = [
    "crates/core",
    "crates/api",
    "crates/cli",
]

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/project"

[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
thiserror = "2"
anyhow = "1"
# Member crate: crates/core/Cargo.toml
[package]
name = "project-core"
version.workspace = true
edition.workspace = true

[dependencies]
serde.workspace = true
thiserror.workspace = true
# WRONG: Duplicating dependency versions across member crates
# crates/core/Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }

# crates/api/Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }  # duplicated

Virtual Workspaces

For projects that are only a collection of crates with no root crate, use a virtual workspace.

# CORRECT: Virtual workspace (no [package] section)
[workspace]
resolver = "2"
members = [
    "crates/*",
]

[workspace.dependencies]
# shared dependency catalog
# WRONG: Root crate that exists only to hold the workspace
[package]
name = "project-root"
version = "0.0.0"
publish = false

[workspace]
members = ["crates/*"]

Feature Flags

Features Must Be Additive

Feature flags must only add functionality, never change or remove existing behavior. Enabling an additional feature should never break code that works without it.

# CORRECT: Features are purely additive
[features]
default = ["json"]
json = ["dep:serde", "dep:serde_json"]
xml = ["dep:quick-xml"]
tracing = ["dep:tracing"]
# WRONG: Mutually exclusive features
[features]
backend-postgres = ["dep:sqlx-postgres"]
backend-sqlite = ["dep:sqlx-sqlite"]
# These cannot be enabled together, violating the additive rule

Use dep: Syntax for Optional Dependencies

Use the dep: syntax to enable optional dependencies without creating implicit feature names.

# CORRECT: dep: syntax (Rust 1.60+)
[features]
json = ["dep:serde", "dep:serde_json"]

[dependencies]
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
# WRONG: Implicit feature names from optional dependencies
[dependencies]
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
# This creates features named "serde" and "serde_json" implicitly

Document Features

Document every feature flag in the crate-level documentation.

// CORRECT: Feature documentation in lib.rs
//! # Features
//!
//! - `json` (default): Enable JSON serialization via serde
//! - `xml`: Enable XML serialization via quick-xml
//! - `tracing`: Enable tracing instrumentation

Dependency Management

Version Requirements

Use semver-compatible version requirements. Prefer caret requirements for flexibility.

# CORRECT: Caret requirements (default, most flexible)
[dependencies]
serde = "1"           # Equivalent to >=1.0.0, <2.0.0
tokio = "1.35"        # Equivalent to >=1.35.0, <2.0.0
uuid = "1.7.0"        # Equivalent to >=1.7.0, <2.0.0
# WRONG: Pinning exact versions (prevents deduplication)
[dependencies]
serde = "=1.0.195"    # Exact pin prevents cargo from resolving conflicts
tokio = "=1.35.1"     # Forces all consumers to use this exact version

Minimize Dependency Features

Only enable the features you actually use.

# CORRECT: Only enable needed features
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "net", "macros"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
# WRONG: Enabling all features when you only need a subset
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = "0.12"  # Pulls in default features including openssl

Dev Dependencies Should Not Leak

Dev dependencies are only used for tests, examples, and benchmarks. They should never appear in [dependencies].

# CORRECT: Test-only dependencies in dev-dependencies
[dev-dependencies]
proptest = "1"
mockall = "0.13"
tempfile = "3"
criterion = { version = "0.5", features = ["html_reports"] }
# WRONG: Test frameworks in production dependencies
[dependencies]
proptest = "1"  # Will be compiled into the production binary

Build Profiles

Release Profile for Binaries Only

Customize [profile.release] only in binary crates (services, CLIs). Libraries should not set release profiles because the consuming binary controls the profile.

# CORRECT: Release profile in a binary crate
[profile.release]
lto = true
codegen-units = 1
strip = true
# WRONG: Release profile in a library crate
# The library does not control how it is compiled; the binary crate does
[package]
name = "my-library"

[profile.release]
lto = true  # Has no effect when used as a dependency

Profile Settings Explained

# Service/CLI production profile
[profile.release]
lto = true          # Link-Time Optimization: slower build, faster binary
codegen-units = 1   # Single codegen unit: slower build, better optimization
strip = true        # Remove debug symbols: smaller binary
panic = "abort"     # Abort on panic: smaller binary, no unwinding overhead

# Development profile tweaks (optional)
[profile.dev]
opt-level = 1       # Slight optimization for faster dev builds

Custom Profiles

Use custom profiles for specific scenarios:

# Fast release builds for CI (skip LTO for speed)
[profile.ci]
inherits = "release"
lto = false
codegen-units = 16

# Profiling build with debug symbols
[profile.profiling]
inherits = "release"
debug = true
strip = false

Publishing

Pre-Publish Checklist

Before publishing to crates.io:

# CORRECT: All required metadata for publishing
[package]
name = "my-crate"
version = "1.0.0"
edition = "2021"
rust-version = "1.75"
description = "A concise description of what this crate does"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/my-crate"
documentation = "https://docs.rs/my-crate"
readme = "README.md"
keywords = ["keyword1", "keyword2"]
categories = ["development-tools"]
exclude = [
    "tests/",
    "benches/",
    ".github/",
    "*.profraw",
]
# WRONG: Missing required metadata
[package]
name = "my-crate"
version = "1.0.0"
# Missing: description, license, repository
# crates.io will reject this or it will have poor discoverability

Exclude Unnecessary Files

Use exclude to keep the published crate small:

# CORRECT: Exclude non-essential files
[package]
exclude = [
    ".github/",
    ".gitignore",
    "tests/fixtures/large-data/",
    "benches/",
    "*.profraw",
    "coverage/",
]

Version Crates Independently in Workspaces

When publishing workspace members, each crate should have its own version even if they share workspace.package.version. Use publish = false for internal-only crates.

# Internal crate not meant for crates.io
[package]
name = "project-internal"
version.workspace = true
publish = false

Cargo.lock Policy

Libraries: Do Not Commit Cargo.lock

Libraries should gitignore Cargo.lock because consumers should resolve their own dependency versions.

# .gitignore for a library
/target
Cargo.lock

Binaries: Commit Cargo.lock

Binary crates (services, CLIs) should commit Cargo.lock for reproducible builds.

# .gitignore for a service/CLI
/target
# Note: Cargo.lock is NOT in .gitignore

Conditional Compilation

Platform-Specific Dependencies

# CORRECT: Platform-specific dependencies
[target.'cfg(unix)'.dependencies]
nix = "0.29"

[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = ["Win32_Foundation"] }

Feature-Gated Dependencies

# CORRECT: Dependencies only compiled when feature is enabled
[dependencies]
serde = { version = "1", optional = true, features = ["derive"] }
serde_json = { version = "1", optional = true }

[features]
json = ["dep:serde", "dep:serde_json"]

Dependency Auditing

cargo-deny Configuration

Use cargo-deny for comprehensive dependency auditing:

# deny.toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"
yanked = "deny"

[licenses]
allow = [
    "MIT",
    "Apache-2.0",
    "BSD-2-Clause",
    "BSD-3-Clause",
    "ISC",
    "Unicode-3.0",
]
confidence-threshold = 0.8

[bans]
multiple-versions = "warn"
wildcards = "deny"

deny = [
    # Deny specific problematic crates
]

[sources]
unknown-registry = "deny"
unknown-git = "deny"
allow-git = []
# WRONG: No dependency auditing configured
# Vulnerable, unlicensed, or duplicate dependencies go unnoticed

Regular Dependency Updates

# Check for outdated dependencies
cargo outdated

# Update within semver bounds
cargo update

# Check for security advisories
cargo audit

# Full dependency check
cargo deny check

Rustfmt Configuration

rustfmt.toml Settings

Keep rustfmt.toml minimal. Most defaults are correct.

# CORRECT: Minimal, intentional rustfmt.toml
edition = "2021"
max_width = 100
use_field_init_shorthand = true
use_try_shorthand = true
# WRONG: Over-configured rustfmt that fights the defaults
edition = "2021"
max_width = 120
tab_spaces = 2
fn_args_layout = "Compressed"
imports_granularity = "Module"
group_imports = "StdExternalCrate"
reorder_imports = false
# Too many non-default settings creates friction for contributors
Stats
Parent Repo Stars0
Parent Repo Forks0
Last CommitFeb 10, 2026