skills/mine/rust-best-practices/SKILL.md
Comprehensive Rust coding guidelines covering ownership, error handling, async patterns, traits, testing, performance, clippy, and documentation. Use when writing new Rust code, reviewing or refactoring existing Rust, implementing async systems with Tokio, designing error hierarchies, choosing between borrowing and cloning, setting up tests or benchmarks, configuring linting, or optimizing performance. Do not use for non-Rust languages or general software architecture unrelated to Rust idioms.
npx skillsauth add pedronauck/skills rust-best-practicesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Unified Rust guidelines covering coding style, ownership, error handling, async patterns, traits, testing, performance, linting, and documentation. Apply when writing or reviewing Rust code.
Load detailed guidance based on context. Read the relevant file when the topic arises:
| Topic | Reference | Load When |
|-------|-----------|-----------|
| Coding Style | references/coding-style.md | Naming, imports, iterators, comments, string handling, macros |
| Error Handling | references/error-handling.md | Result, Option, ?, thiserror, anyhow, custom errors, async errors |
| Ownership & Pointers | references/ownership-and-pointers.md | Lifetimes, borrowing, smart pointers, Pin, Cow, interior mutability |
| Traits & Generics | references/traits-and-generics.md | Trait design, dispatch, GATs, sealed traits, type state pattern |
| Async & Concurrency | references/async-and-concurrency.md | Tokio, channels, streams, shutdown, runtime config, async traits |
| Sync Concurrency | references/concurrency-sync.md | Atomics, Mutex, RwLock, lock ordering, Send/Sync, memory ordering |
| Testing | references/testing.md | Unit/integration/doc tests, snapshot, proptest, mockall, benchmarks, fuzz |
| Performance | references/performance.md | Profiling, flamegraph, cloning, stack vs heap, iterators, allocation |
| Clippy & Linting | references/clippy-and-linting.md | Clippy config, key lints, workspace setup, #[expect] vs #[allow] |
| Documentation | references/documentation.md | Doc comments, rustdoc, doc lints, coverage checklist |
&T over .clone() unless ownership transfer is required&str over String, &[T] over Vec<T> in function parametersget_ prefix on getters: fn name() not fn get_name()as_ (cheap borrow), to_ (expensive/cloning), into_ (ownership transfer)iter() / iter_mut() / into_iter()std -> external crates -> workspace crates -> super:: -> crate::format! over string concatenation with +s.bytes() over s.chars() for ASCII-only operationsResult<T, E> for fallible operations; reserve panic! for unrecoverable bugsunwrap() in production. Use expect() with descriptive message only when the value is logically guaranteed. Prefer ?, if let, let...else for all other casesthiserror for library/crate errors, anyhow for binaries only? operator over match chains for error propagation_else variants (ok_or_else, unwrap_or_else) to prevent eager allocationinspect_err and map_err for logging and transforming errorsassert! at function entry for invariant checking (debug builds)Copy types (<=24 bytes, all fields Copy, no heap) pass by valueCow<'_, T> when data may or may not need ownership'src, 'ctx, 'conn — not just 'atry_borrow() on RefCell to avoid panics; prefer over direct .borrow_mut()let x = x.parse()?| Pointer | When to Use |
|---------|-------------|
| Box<T> | Single ownership, heap allocation, recursive types |
| Rc<T> | Shared ownership, single-threaded |
| Arc<T> | Shared ownership, multi-threaded |
| Cell<T> / RefCell<T> | Interior mutability, single-threaded |
| Mutex<T> / RwLock<T> | Interior mutability, multi-threaded |
dyn Trait only when heterogeneous collections or plugin architectures are neededSelf: Sized, methods use &self/&mut self/selfstruct Connection<S> { _state: PhantomData<S> }
struct Disconnected;
struct Connected;
impl Connection<Connected> { fn send(&self, data: &[u8]) { /* ... */ } }
.await points — use scoped guardsstd::thread::sleep in async — use tokio::time::sleepSend bounds on spawned futuresJoinSet for managing multiple concurrent tasksCancellationToken (from tokio_util) for graceful shutdowntracing + #[instrument] for async debugging| Channel | Use Case |
|---------|----------|
| mpsc | Multi-producer, single-consumer message passing |
| broadcast | Multi-producer, multi-consumer event fan-out |
| oneshot | Single value, single use (request-response) |
| watch | Latest-value-only, change notification |
crossbeam::channel over std::sync::mpsctokio::sync::{mpsc, broadcast, oneshot, watch}AtomicBool, AtomicUsize) over Mutex for primitive typesRelaxed / Acquire / Release / SeqCstprocess_should_return_error_when_input_empty()mod blocks by unit of work///) for public API examples; run separately with cargo test --doccargo insta test then cargo insta review; redact unstable fieldsrstest for parameterized tests with #[case::name] labelsproptest for property-based testing with custom strategiesmockall with #[automock] for mocking traitscriterion for benchmarks with iter_batched and BenchmarkIdcargo-fuzz with libfuzzer_sys for fuzz testingcargo-tarpaulin or cargo-llvm-cov for code coveragesqlx::test for database integration tests with automatic pool injection#[should_panic] and #[ignore] attributes where appropriate--releasecargo clippy -- -D clippy::perf for performance-related hintscargo flamegraph or samply (macOS) for profilingVec::with_capacity(), String::with_capacity()for loops; avoid intermediate .collect()smallvec for large const arraysCow<'_, T> to avoid unnecessary allocations.bytes() over s.chars() for ASCII-only string operationsRun regularly:
cargo clippy --all-targets --all-features --locked -- -D warnings
| Lint | Catches |
|------|---------|
| redundant_clone | Unnecessary .clone() calls |
| needless_borrow | Unnecessary & borrows |
| large_enum_variant | Oversized variants (consider Box) |
| needless_collect | Premature .collect() before iteration |
| map_unwrap_or | .map().unwrap_or() chains |
| unnecessary_wraps | Functions always returning Ok/Some |
| clone_on_copy | .clone() on Copy types |
#[expect(clippy::lint)] over #[allow(...)] — expect warns when lint no longer applies#![warn(clippy::all)] as workspace minimumCargo.toml with priority levels// comments explain why: safety invariants, workarounds, design rationale/// doc comments explain what and how for all public items//! for module-level and crate-level documentation at top of lib.rs/mod.rsTODO needs a linked issue: // TODO(#42): description#![deny(missing_docs)] for libraries# Examples, # Errors, # Panics, # Safety sections in doc comments| Doc Lint | Purpose |
|----------|---------|
| missing_docs | Ensure all public items documented |
| broken_intra_doc_links | Catch dead cross-references |
| missing_panics_doc | Document panic conditions |
| missing_errors_doc | Document error conditions |
| missing_safety_doc | Document unsafe safety requirements |
struct Email(String)if let [first, .., last] = sliceVec when length is known at compile timelet x = x.parse()?Cow<str> when data might need modification of borrowed datacontains() on strings is O(n*m) — avoid nested string iteration| Deprecated | Better | Since |
|------------|--------|-------|
| lazy_static! | std::sync::OnceLock | Rust 1.70 |
| once_cell::Lazy | std::sync::LazyLock | Rust 1.80 |
| std::sync::mpsc | crossbeam::channel (sync) | — |
| std::sync::Mutex | parking_lot::Mutex (recommended) | — |
| failure / error-chain | thiserror / anyhow | — |
| try!() | ? operator | Rust 2018 |
| async-trait crate | Native async fn in traits (1.75+, limited) | Rust 1.75 |
Recommended dependencies:
[dependencies]
thiserror = "2"
anyhow = "1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = "0.3"
[dev-dependencies]
rstest = "0.25"
proptest = "1"
mockall = "0.13"
criterion = { version = "0.5", features = ["html_reports"] }
insta = { version = "1", features = ["yaml"] }
Workspace lints (Cargo.toml):
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
rustfmt.toml:
reorder_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
Result/Option — no silent failuresthiserror for library errors, anyhow for binariesunsafe code; document all unsafe blocks with safety invariantscargo clippy and fix all warningscargo fmt for consistent formatting/// documentation with examples for all public itemstracing for observability in async coderstest, proptest, insta, mockall, criterion) — even if the user did not ask for testsunwrap() in production codeunsafe without documented safety invariants#[expect(...)] and justification.await pointsstd::thread::sleep in async contextpanic! for recoverable errorsString where &str suffices; clone unnecessarilyspawn_blockingdevelopment
Deep review of branch diffs, working trees, or GitHub PRs at any size. Use when the user asks for CodeRabbit-grade review, an incremental re-review after new pushes, publication of findings to a PR, a cross-LLM peer-review verdict round, or conformance review against spec artifacts. Don't use for applying fixes, reviewing specs or PRDs as documents, or quick single-file feedback.
tools
Orchestrate Claude and Codex worker TUIs from a controller agent through herdr panes and the herdr socket CLI. Use when delegating bounded tasks to herdr worker panes, running user-activated plan-first delegations (Claude Code plan mode, Codex Plan mode), waiting on native agent status (idle, working, blocked, done), or verifying worker reports. Workers launch as interactive TUIs via herdr agent start — never through headless runners (compozy exec, claude -p, codex exec). Not for cmux workspaces (see cmux-orchestration) and not for end-user herdr control.
tools
TanStack Query, Router, and Form patterns for React. Use when writing useQuery/queryOptions, mutations, caching, file-based routes, search params, loaders, or TanStack Form validation. Don't use for TanStack Start, TanStack DB/collections, Zustand client state, or non-TanStack routing.
development
Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.