From rust-dev
This skill should be used when the user asks about "rust workspace", "rust best practices", "cargo workspace setup", "rust code organization", "rust dependency management", "rust testing strategy", "rust project", "scalable rust", "rust CI setup", or needs guidance on senior-level Rust development patterns, workspace design, code organization strategies, or production-ready Rust architectures.
npx claudepluginhub ClementWalter/rookie-marketplace --plugin rust-devThis skill uses the workspace's default tool permissions.
Battle-tested patterns for Rust workspace architecture, code organization, dependencies, and testing that scale from prototype to production.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Battle-tested patterns for Rust workspace architecture, code organization, dependencies, and testing that scale from prototype to production.
All coding work MUST happen in git worktrees. Before making any code changes:
git worktree add ~/.claude/worktrees/$(basename $(pwd))/<task> -b feat/<task>/merge to consolidate changes back to mainNever edit files directly in the main worktree.
Before completing ANY Rust task, you MUST:
cargo test --workspacetrunk checkIf trunk has formatting issues, run trunk fmt to auto-fix.
Use a Rust workspace when you have:
Canonical workspace structure:
repo/
Cargo.toml # workspace root
crates/
core/ # pure domain logic (no IO)
storage/ # DB, filesystem, etc.
api/ # HTTP/GRPC handlers, DTOs
cli/ # binary
tools/ # optional: internal binaries (codegen, migration, etc.)
tests/ # optional: black-box integration tests
Layered architecture:
Critical rule: If core imports tokio, reqwest, or sqlx, you've already lost the separation.
Too many crates is busywork. Start with 2–5 max.
Split only when:
In root Cargo.toml, use workspace dependencies to keep versions aligned:
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.dependencies]
anyhow = "*" # use latest
thiserror = "*" # use latest
serde = { version = "*", features = ["derive"] } # use latest
tokio = { version = "*", features = ["macros", "rt-multi-thread"] } # use latest
In crate Cargo.toml:
[dependencies]
serde = { workspace = true }
This reduces version drift and security churn.
Pattern for optional dependencies:
[dependencies]
sqlx = { workspace = true, optional = true }
[features]
db = ["dep:sqlx"]
rust-toolchain.tomlOrganize by capability / domain, not by "models/handlers/utils" spaghetti.
Good organization:
core/
src/
lib.rs
payment/
mod.rs
validation.rs
pricing.rs
user/
mod.rs
id.rs
rules.rs
Avoid:
models.rs
handlers.rs
utils.rs
pub(crate) by defaultlib.rsmod payment;
pub use payment::{Payment, PaymentError};
If everything is pub you've created an accidental framework.
Preludes tend to hide dependencies and make code review harder. Prefer explicit imports.
Common approach:
thiserror for typed errorsanyhow at the top levelDon't leak anyhow::Error across library boundaries unless you explicitly want "opaque".
If you can keep core synchronous and pure, you gain:
Every dependency adds:
Prefer "boring" crates with strong maintenance.
cargo-deny + cargo-auditMake dependency issues visible early (licenses, advisories, duplicate versions).
unwrap() in LibrariesIn binaries/tests it's fine (especially in test scaffolding). In libraries, return errors with context.
Think "pyramid":
mod tests {} in the same file for private accessUse crates/<crate>/tests/*.rs for API-level tests.
If you have a service:
proptest for invariants ("decode(encode(x)) == x")cargo-fuzz for parsers/decoders/inputs from outsideDoctests enforce that examples compile and keep your public API honest.
println! - Use Tracing InsteadNEVER use println!, eprintln!, or dbg! for output. Always use the tracing crate:
use tracing::{debug, info, warn, error, trace};
// Good - structured logging
info!("Processing request for user {user_id}");
debug!("Cache hit: {key}");
warn!("Retry attempt {attempt} of {max_retries}");
error!("Failed to connect: {err}");
// Bad - never do this
println!("Processing request for user {}", user_id);
dbg!(value);
Why:
test-log for TestsAlways use test_log::test attribute for tests to capture tracing output:
use test_log::test;
#[test]
fn test_something() {
info!("This will be visible when test fails or with --nocapture");
assert!(true);
}
#[test(tokio::test)]
async fn test_async_something() {
debug!("Async test with tracing");
}
Add to Cargo.toml (use latest versions):
[dev-dependencies]
test-log = { version = "*", features = ["trace"] } # use latest
tracing-subscriber = { version = "*", features = ["env-filter"] } # use latest
Run tests with visible logs: RUST_LOG=debug cargo test -- --nocapture
clippy::uninlined_format_args)Always use variables directly in format strings instead of passing them as arguments:
// Good - variable inlined
let name = "world";
info!("Hello, {name}!");
format!("Value: {value}, Count: {count}")
// Bad - uninlined arguments
info!("Hello, {}!", name);
format!("Value: {}, Count: {}", value, count)
This improves readability and reduces potential argument ordering mistakes.
cargo fmt --check
cargo clippy --all-targets --all-features -D warnings
cargo test --workspace --all-features
Additional gates:
cargo deny / cargo auditcargo llvm-cov for coverage, but don't worship %resolver = "2" and avoid unnecessary default featuresOne-way dependencies:
core → (nothing)adapters → coreapp → adapters + corebin → appVisibility:
IO placement:
coreTest distribution:
Tooling:
CLI: Thin binary → lib (for testability)
Services: Separate protocol definitions; feature-flag transport layers
ZK/crypto: Isolate no_std core; separate proving/verification crates
WASM: Separate bindings; platform-agnostic core