From rc
Rust as a language — ownership and lifetimes, error hierarchies with thiserror/anyhow, async with Tokio, trait design, testing, performance, clippy, and rustdoc. Use when writing or reviewing Rust code, deciding between borrowing and cloning, designing an error type, structuring async tasks and channels, or configuring lints and benchmarks. Do not use for Axum HTTP routing and middleware (rc-axum), SQLx/Postgres data access (rc-sqlx), SvelteKit (rc-sveltekit), or non-Rust languages.
How this skill is triggered — by the user, by Claude, or both
Slash command
/rc:rc-rustThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Unified Rust guidelines covering coding style, ownership, error handling, async patterns, traits, testing, performance, linting, and documentation. Apply when writing or reviewing Rust code.
Unified Rust guidelines covering coding style, ownership, error handling, async patterns, traits, testing, performance, linting, and documentation. Apply when writing or reviewing Rust code.
This skill owns Rust the language. The stack's layers live elsewhere — reach for them instead of duplicating their guidance here:
rc-axum — HTTP routing, extractors, Tower middleware, IntoResponse error mapping, WebSocketsrc-sqlx — PgPool, query macros, transactions, migrations, #[sqlx::test]rc-sveltekit — the SSR front end that consumes these APIsrc-fullstack-axum-svelte — the router when a change spans more than one layerrc-final-verify — after cargo fmt/clippy/test come back greenLoad 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"
rstest, proptest, insta, mockall, criterion) — even if the user did not ask for testsnpx claudepluginhub rodolfochicone/rc-project --plugin rcEnforces idiomatic Rust patterns for ownership/borrowing, error handling with anyhow/thiserror, enums, traits, concurrency, and crate design.
Provides idiomatic Rust patterns for ownership, error handling, traits, concurrency, and best practices for building safe, performant applications.
Provides 265 Rust coding rules across 26 categories for writing, reviewing, and refactoring Rust code. Covers ownership, error handling, async, concurrency, unsafe code, API design, memory, performance, and testing.