by leonardomso
A collection of 179 rules that AI coding agents can use when writing Rust.
# Add to your Claude Code skills
git clone https://github.com/leonardomso/rust-skillsComprehensive guide for writing high-quality, idiomatic, and highly optimized Rust code. Contains 179 rules across 14 categories, prioritized by impact to guide LLMs in code generation and refactoring.
Reference these guidelines when:
| Priority | Category | Impact | Prefix | Rules |
|----------|----------|--------|--------|-------|
| 1 | Ownership & Borrowing | CRITICAL | own- | 12 |
| 2 | Error Handling | CRITICAL | err- | 12 |
| 3 | Memory Optimization | CRITICAL | mem- | 15 |
| 4 | API Design | HIGH | api- | 15 |
| 5 | Async/Await | HIGH | async- | 15 |
| 6 | Compiler Optimization | HIGH | opt- | 12 |
| 7 | Naming Conventions | MEDIUM | name- | 16 |
| 8 | Type Safety | MEDIUM | type- | 10 |
| 9 | Testing | MEDIUM | test- | 13 |
| 10 | Documentation | MEDIUM | doc- | 11 |
| 11 | Performance Patterns | MEDIUM | perf- | 11 |
| 12 | Project Structure | LOW | proj- | 11 |
| 13 | Clippy & Linting | LOW | lint- | 11 |
| 14 | Anti-patterns | REFERENCE | anti- | 15 |
own-borrow-over-clone - Prefer borrowing over 179 Rust rules your AI coding agent can use to write better code.
Works with Claude Code, Cursor, Windsurf, Copilot, Codex, Aider, Zed, Amp, Cline, and pretty much any other agent that supports skills.
npx add-skill leonardomso/rust-skills
That's it. The CLI figures out which agents you have and installs the skill to the right place.
After installing, just ask your agent:
/rust-skills review this function
/rust-skills is my error handling idiomatic?
/rust-skills check for memory issues
The agent loads the relevant rules and applies them to your code.
179 rules split into 14 categories:
| Category | Rules | What it covers |
|----------|-------|----------------|
| Ownership & Borrowing | 12 | When to borrow vs clone, Arc/Rc, lifetimes |
| Error Handling | 12 | thiserror for libs, anyhow for apps, the ? operator |
| Memory | 15 | SmallVec, arenas, avoiding allocations |
| API Design | 15 | Builder pattern, newtypes, sealed traits |
| Async | 15 | Tokio patterns, channels, spawn_blocking |
| Optimization | 12 | LTO, inlining, PGO, SIMD |
| Naming | 16 | Following Rust API Guidelines |
| Type Safety | 10 | Newtypes, parse don't validate |
| Testing | 13 | Proptest, mockall, criterion |
| Docs | 11 | Doc examples, intra-doc links |
| Performance | 11 | Iterators, entry API, collect patterns |
| Project Structure | 11 | Workspaces, module layout |
| | 11 | Clippy config, CI setup |
| | 15 | Common mistakes and how to fix them |
No comments yet. Be the first to share your thoughts!
&T.clone()own-slice-over-vec - Accept &[T] not &Vec<T>, &str not &Stringown-cow-conditional - Use Cow<'a, T> for conditional ownershipown-arc-shared - Use Arc<T> for thread-safe shared ownershipown-rc-single-thread - Use Rc<T> for single-threaded sharingown-refcell-interior - Use RefCell<T> for interior mutability (single-thread)own-mutex-interior - Use Mutex<T> for interior mutability (multi-thread)own-rwlock-readers - Use RwLock<T> when reads dominate writesown-copy-small - Derive Copy for small, trivial typesown-clone-explicit - Make Clone explicit, avoid implicit copiesown-move-large - Move large data instead of cloningown-lifetime-elision - Rely on lifetime elision when possibleerr-thiserror-lib - Use thiserror for library error typeserr-anyhow-app - Use anyhow for application error handlingerr-result-over-panic - Return Result, don't panic on expected errorserr-context-chain - Add context with .context() or .with_context()err-no-unwrap-prod - Never use .unwrap() in production codeerr-expect-bugs-only - Use .expect() only for programming errorserr-question-mark - Use ? operator for clean propagationerr-from-impl - Use #[from] for automatic error conversionerr-source-chain - Use #[source] to chain underlying errorserr-lowercase-msg - Error messages: lowercase, no trailing punctuationerr-doc-errors - Document errors with # Errors sectionerr-custom-type - Create custom error types, not Box<dyn Error>mem-with-capacity - Use with_capacity() when size is knownmem-smallvec - Use SmallVec for usually-small collectionsmem-arrayvec - Use ArrayVec for bounded-size collectionsmem-box-large-variant - Box large enum variants to reduce type sizemem-boxed-slice - Use Box<[T]> instead of Vec<T> when fixedmem-thinvec - Use ThinVec for often-empty vectorsmem-clone-from - Use clone_from() to reuse allocationsmem-reuse-collections - Reuse collections with clear() in loopsmem-avoid-format - Avoid format!() when string literals workmem-write-over-format - Use write!() instead of format!()mem-arena-allocator - Use arena allocators for batch allocationsmem-zero-copy - Use zero-copy patterns with slices and Bytesmem-compact-string - Use CompactString for small string optimizationmem-smaller-integers - Use smallest integer type that fitsmem-assert-type-size - Assert hot type sizes to prevent regressionsapi-builder-pattern - Use Builder pattern for complex constructionapi-builder-must-use - Add #[must_use] to builder typesapi-newtype-safety - Use newtypes for type-safe distinctionsapi-typestate - Use typestate for compile-time state machinesapi-sealed-trait - Seal traits to prevent external implementationsapi-extension-trait - Use extension traits to add methods to foreign typesapi-parse-dont-validate - Parse into validated types at boundariesapi-impl-into - Accept impl Into<T> for flexible string inputsapi-impl-asref - Accept impl AsRef<T> for borrowed inputsapi-must-use - Add #[must_use] to Result returning functionsapi-non-exhaustive - Use #[non_exhaustive] for future-proof enums/structsapi-from-not-into - Implement From, not Into (auto-derived)api-default-impl - Implement Default for sensible defaultsapi-common-traits - Implement Debug, Clone, PartialEq eagerlyapi-serde-optional - Gate Serialize/Deserialize behind feature flagasync-tokio-runtime - Use Tokio for production async runtimeasync-no-lock-await - Never hold Mutex/RwLock across .awaitasync-spawn-blocking - Use spawn_blocking for CPU-intensive workasync-tokio-fs - Use tokio::fs not std::fs in async codeasync-cancellation-token - Use CancellationToken for graceful shutdownasync-join-parallel - Use tokio::join! for parallel operationsasync-try-join - Use tokio::try_join! for fallible parallel opsasync-select-racing - Use tokio::select! for racing/timeoutsasync-bounded-channel - Use bounded channels for backpressureasync-mpsc-queue - Use mpsc for work queuesasync-broadcast-pubsub - Use broadcast for pub/sub patternsasync-watch-latest - Use watch for latest-value sharingasync-oneshot-response - Use oneshot for request/responseasync-joinset-structured - Use JoinSet for dynamic task groupsasync-clone-before-await - Clone data before await, release locksopt-inline-small - Use #[inline] for small hot functionsopt-inline-always-rare - Use #[inline(always)] sparinglyopt-inline-never-cold - Use #[inline(never)] for cold pathsopt-cold-unlikely - Use #[cold] for error/unlikely pathsopt-likely-hint - Use likely()/unlikely() for branch hintsopt-lto-release - Enable LTO in release buildsopt-codegen-units - Use codegen-units = 1 for max optimizationopt-pgo-profile - Use PGO for production buildsopt-target-cpu - Set target-cpu=native for local buildsopt-bounds-check - Use iterators to avoid bounds checksopt-simd-portable - Use portable SIMD for data-parallel opsopt-cache-friendly - Design cache-friendly data layouts (SoA)name-types-camel - Use UpperCamelCase for types, traits, enumsname-variants-camel - Use UpperCamelCase for enum variantsname-funcs-snake - Use snake_case for functions, methods, modulesname-consts-screaming - Use SCREAMING_SNAKE_CASE for constants/staticsname-lifetime-short - Use short lowercase lifetimes: 'a, 'de, 'srcname-type-param-single - Use single uppercase for type params: T, E, K, Vname-as-free - as_ prefix: free reference conversionname-to-expensive - to_ prefix: expensive conversionname-into-ownership - into_ prefix: ownership transfername-no-get-prefix - No get_ prefix for simple gettersname-is-has-bool - Use is_, has_, can_ for boolean methodsname-iter-convention - Use iter/iter_mut/into_iter for iteratorsname-iter-method - Name iterator methods consistentlyname-iter-type-match - Iterator type names match methodname-acronym-word - Treat acronyms as words: Uuid not UUIDname-crate-no-rs - Crate names: no -rs suffixtype-newtype-ids - Wrap IDs in newtypes: UserId(u64)type-newtype-validated - Newtypes for validated data: Email, Urltype-enum-states - Use enums for mutually exclusive statestype-option-nullable - Use Option<T> for nullable valuestype-result-fallible - Use Result<T, E> for fallible operationstype-phantom-marker - Use PhantomData<T> for type-level markerstype-never-diverge - Use ! type for functions that never returntype-generic-bounds - Add trait bounds only where neededtype-no-stringly - Avoid stringly-typed APIs, use enums/newtypestype-repr-transparent - Use #[repr(transparent)] for FFI newtypestest-cfg-test-module - Use #[cfg(test)] mod tests { }test-use-super - Use use super::*; in test modulestest-integration-dir - Put integration tests in tests/ directorytest-descriptive-names - Use descriptive test namestest-arrange-act-assert - Structure tests as arrange/act/asserttest-proptest-properties - Use proptest for property-based testingtest-mockall-mocking - Use mockall for trait mockingtest-mock-traits - Use traits for dependencies to enable mockingtest-fixture-raii - Use RAII pattern (Drop) for test cleanuptest-tokio-async - Use #[tokio::test] for async teststest-should-panic - Use #[should_panic] for panic teststest-criterion-bench - Use criterion for benchmarkingtest-doctest-examples - Keep doc examples as executable testsdoc-all-public - Document all public items with ///doc-module-inner - Use //! for module-level documentationdoc-examples-section - Include # Examples with runnable codedoc-errors-section - Include # Errors for fallible functionsdoc-panics-section - Include # Panics for panicking functionsdoc-safety-section - Include # Safety for unsafe functionsdoc-question-mark - Use ? in examples, not .unwrap()doc-hidden-setup - Use # prefix to hide example setup codedoc-intra-links - Use intra-doc links: [Vec]doc-link-types - Link related types and functions in docsdoc-cargo-metadata - Fill Cargo.toml metadataperf-iter-over-index - Prefer iterators over manual indexingperf-iter-lazy - Keep iterators lazy, collect() only when neededperf-collect-once - Don't collect() intermediate iteratorsperf-entry-api - Use entry() API for map insert-or-updateperf-drain-reuse - Use drain() to reuse allocationsperf-extend-batch - Use extend() for batch insertionsperf-chain-avoid - Avoid chain() in hot loopsperf-collect-into - Use collect_into() for reusing containersperf-black-box-bench - Use black_box() in benchmarksperf-release-profile - Optimize release profile settingsperf-profile-first - Profile before optimizingproj-lib-main-split - Keep main.rs minimal, logic in lib.rsproj-mod-by-feature - Organize modules by feature, not typeproj-flat-small - Keep small projects flatproj-mod-rs-dir - Use mod.rs for multi-file modulesproj-pub-crate-internal - Use pub(crate) for internal APIsproj-pub-super-parent - Use pub(super) for parent-only visibilityproj-pub-use-reexport - Use pub use for clean public APIproj-prelude-module - Create prelude module for common importsproj-bin-dir - Put multiple binaries in src/bin/proj-workspace-large - Use workspaces for large projectsproj-workspace-deps - Use workspace dependency inheritancelint-deny-correctness - #![deny(clippy::correctness)]lint-warn-suspicious - #![warn(clippy::suspicious)]lint-warn-style - #![warn(clippy::style)]lint-warn-complexity - #![warn(clippy::complexity)]lint-warn-perf - #![warn(clippy::perf)]lint-pedantic-selective - Enable clippy::pedantic selectivelylint-missing-docs - #![warn(missing_docs)]lint-unsafe-doc - #![warn(clippy::undocumented_unsafe_blocks)]lint-cargo-metadata - #![warn(clippy::cargo)] for published crateslint-rustfmt-check - Run cargo fmt --check in CIlint-workspace-lints - Configure lints at workspace levelanti-unwrap-abuse - Don't use .unwrap() in production codeanti-expect-lazy - Don't use .expect() for recoverable errorsanti-clone-excessive - Don't clone when borrowing worksanti-lock-across-await - Don't hold locks across .awaitanti-string-for-str - Don't accept &String when &str worksanti-vec-for-slice - Don't accept &Vec<T> when &[T] worksanti-index-over-iter - Don't use indexing when iterators workanti-panic-expected - Don't panic on expected/recoverable errorsanti-empty-catch - Don't use empty if let Err(_) = ... blocksanti-over-abstraction - Don't over-abstract with excessive genericsanti-premature-optimize - Don't optimize before profilinganti-type-erasure - Don't use Box<dyn Trait> when impl Trait worksanti-format-hot-path - Don't use format!() in hot pathsanti-collect-intermediate - Don't collect() intermediate iteratorsanti-stringly-typed - Don't use strings for structured data[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
[profile.bench]
inherits = "release"
debug = true
strip = false
[profile.dev]
opt-level = 0
debug = true
[profile.dev.package."*"]
opt-level = 3 # Optimize dependencies in dev
This skill provides rule identifiers for quick reference. When generating or reviewing Rust code:
rules/ for detailed examples| Task | Primary Categories |
|------|-------------------|
| New function | own-, err-, name- |
| New struct/API | api-, type-, doc- |
| Async code | async-, own- |
| Error handling | err-, api- |
| Memory optimization | mem-, own-, perf- |
| Performance tuning | opt-, mem-, perf- |
| Code review | anti-, lint- |
This skill synthesizes best practices from:
Each rule has:
If add-skill doesn't work for your setup, here's how to install manually:
Global (applies to all projects):
git clone https://github.com/leonardomso/rust-skills.git ~/.claude/skills/rust-skills
Or just for one project:
git clone https://github.com/leonardomso/rust-skills.git .claude/skills/rust-skills
git clone https://github.com/leonardomso/rust-skills.git .opencode/skills/rust-skills
git clone https://github.com/leonardomso/rust-skills.git .cursor/skills/rust-skills
Or just grab the skill file:
curl -o .cursorrules https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
mkdir -p .windsurf/rules
curl -o .windsurf/rules/rust-skills.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
git clone https://github.com/leonardomso/rust-skills.git .codex/skills/rust-skills
Or use the AGENTS.md standard:
curl...