by leonardomso
A collection of 265 rules across 26 categories that AI coding agents can use to write idiomatic, fast, and safe Rust.
# Add to your Claude Code skills
git clone https://github.com/leonardomso/rust-skillsGuides for using ai agents skills like rust-skills.
Comprehensive guide for writing high-quality, idiomatic, and highly optimized Rust code. Contains 265 rules across 26 categories, prioritized by impact to guide LLMs in code generation and refactoring. Current for Rust 1.96 (2024 edition).
Reference these guidelines when:
unsafe code| Priority | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | Ownership & Borrowing | CRITICAL | own- |
12 |
| 2 | Error Handling | CRITICAL | err- |
12 |
| 3 | Memory Optimization | CRITICAL | mem- |
17 |
| 4 | Unsafe Code | CRITICAL | unsafe- |
7 |
| 5 | API Design | HIGH | api- |
17 |
| 6 | Async/Await | HIGH | async- |
18 |
| 7 | Concurrency | HIGH | conc- |
4 |
| 8 | Compiler Optimization | HIGH | opt- |
12 |
| 9 | Numeric & Arithmetic Safety | HIGH | num- |
5 |
| 10 | Type Safety | MEDIUM | type- |
13 |
| 11 | Trait & Generics Design | MEDIUM | trait- |
6 |
| 12 | Conversions | MEDIUM | conv- |
3 |
| 13 | Const & Compile-Time | MEDIUM | const- |
4 |
| 14 | Serde | MEDIUM | serde- |
8 |
| 15 | Pattern Matching | MEDIUM | pat- |
5 |
| 16 | Macros | MEDIUM | macro- |
8 |
| 17 | Closures | MEDIUM | closure- |
5 |
| 18 | Collections | MEDIUM | coll- |
4 |
| 19 | Naming Conventions | MEDIUM | name- |
16 |
| 20 | Testing | MEDIUM | test- |
15 |
| 21 | Documentation | MEDIUM | doc- |
12 |
| 22 | Observability | MEDIUM | obs- |
7 |
| 23 | Performance Patterns | MEDIUM | perf- |
13 |
| 24 | Project Structure | LOW | proj- |
14 |
| 25 | Clippy & Linting | LOW | lint- |
13 |
| 26 | Anti-patterns | REFERENCE | anti- |
15 |
own-borrow-over-clone - Prefer &T borrowing over .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 shared ownership in single-threaded contextsown-refcell-interior - Use RefCell<T> for interior mutability in single-threaded codeown-mutex-interior - Use Mutex<T> for interior mutability across threadsown-rwlock-readers - Use RwLock<T> when reads significantly outnumber writesown-copy-small - Implement Copy for small, simple typesown-clone-explicit - Use explicit Clone for types where copying has meaningful costown-move-large - Move large types instead of copying; use Box if moves are expensiveown-lifetime-elision - Rely on lifetime elision rules; add explicit lifetimes only when requirederr-thiserror-lib - Use thiserror for library error typeserr-anyhow-app - Use anyhow for application error handlingerr-result-over-panic - Return Result<T, E> instead of panicking for recoverable errorserr-context-chain - Add context with .context() or .with_context()err-no-unwrap-prod - Avoid unwrap() in production code; use ?, expect(), or handle errorserr-expect-bugs-only - Use expect() only for invariants that indicate bugs, not user errorserr-question-mark - Use ? operator for clean propagationerr-from-impl - Implement From<E> for error conversions to enable ? operatorerr-source-chain - Preserve error chains with #[source] or source() methoderr-lowercase-msg - Start error messages lowercase, no trailing punctuationerr-doc-errors - Document error conditions with # Errors section in doc commentserr-custom-type - Define custom error types for domain-specific failuresmem-with-capacity - Use with_capacity() when size is knownmem-smallvec - Use SmallVec for usually-small collectionsmem-arrayvec - Use ArrayVec<T, N> for fixed-capacity collections that never heap-allocatemem-box-large-variant - Box large enum variants to reduce overall enum sizemem-boxed-slice - Use Box<[T]> instead of Vec<T> for fixed-size heap datamem-thinvec - Use ThinVec<T> for nullable collections with minimal overheadmem-clone-from - Use clone_from() to reuse allocations when repeatedly cloningmem-reuse-collections - Clear and reuse collections instead of creating new ones in loopsmem-avoid-format - Avoid format!() when string literals workmem-write-over-format - Use write!() into existing buffers instead of format!() allocationsmem-arena-allocator - Use arena allocators for batch allocationsmem-zero-copy - Use zero-copy patterns with slices and Bytesmem-compact-string - Use compact string types for memory-constrained string storagemem-smaller-integers - Use appropriately-sized integers to reduce memory footprintmem-assert-type-size - Use static assertions to guard against accidental type size growthmem-take-replace - Use mem::take / mem::replace to move a value out of a &mut without cloningmem-drop-order - Know and control drop order: struct fields drop top-to-bottom, locals in reverseunsafe-safety-comment - Write a // SAFETY: comment above every unsafe block and a # Safety section in every unsafe fn.unsafe-minimize-scope - Keep unsafe blocks as small as possible — mark only the operation that requires unsafety, not the surrounding safe code.unsafe-miri-ci - Run cargo miri test in CI for every crate that contains unsafe code.unsafe-maybeuninit - Use MaybeUninit<T> for uninitialized memory; never use mem::uninitialized() or mem::zeroed() for types with validity invariants.unsafe-extern-block - In Rust 2024, wrap extern blocks in unsafe extern { } and annotate each item as safe or unsafe.unsafe-send-sync-manual - Document the invariants when manually implementing Send or Sync; prefer letting the compiler derive them automatically.unsafe-no-mangle-unsafe - In Rust 2024, write #[unsafe(no_mangle)], #[unsafe(export_name = "...")], and #[unsafe(link_section = "...")] — not the bare attribute forms.api-builder-pattern - Use Builder pattern for complex constructionapi-builder-must-use - Mark builder methods with #[must_use] to prevent silent dropsapi-newtype-safety - Use newtypes to prevent mixing semantically different valuesapi-typestate - Use typestate pattern to encode state machine invariants in the type systemapi-sealed-trait - Use sealed traits to prevent external implementations while allowing useapi-extension-trait - Use extension traits to add methods to external typesapi-parse-dont-validate - Parse into validated types at boundariesapi-impl-into - Accept impl Into<T> for flexible APIs, implement From<T> for conversionsapi-impl-asref - Use AsRef<T> when you only need to borrow the inner dataapi-must-use - Mark types and functions with #[must_use] when ignoring results is likely a bugapi-non-exhaustive - Use #[non_exhaustive] on public enums and structs for forward compatibilityapi-from-not-into - Implement From<T>, not Into<U> - From gives you Into for freeapi-default-impl - Implement Default for types with sensible default valuesapi-common-traits - Implement standard traits (Debug, Clone, PartialEq, etc.) for public typesapi-serde-optional - Make serde a feature flag, not a hard dependency for library cratesapi-impl-fromiterator - Implement FromIterator and Extend for collection types, and IntoIterator for all three reference formsapi-operator-overload - Overload operators only when the semantics are natural and unsurprisingasync-tokio-runtime - Configure Tokio runtime appropriately for your workloadasync-no-lock-await - Never hold Mutex/RwLock across .awaitasync-spawn-blocking - Use spawn_blocking for CPU-intensive workasync-tokio-fs - Use tokio::fs instead of std::fs in async codeasync-cancellation-token - Use CancellationToken for graceful shutdown and task cancellationasync-join-parallel - Use join! or try_join! for concurrent independent futuresasync-try-join - Use try_join! for concurrent fallible operations with early return on errorasync-select-racing - Use select! to race futures and handle the first to completeasync-bounded-channel - Use bounded channels to apply backpressure and prevent unbounded memory growthasync-mpsc-queue - Use mpsc channels for async message queues between tasksasync-broadcast-pubsub - Use broadcast channel for pub/sub where all subscribers receive all messagesasync-watch-latest - Use watch channel for sharing the latest value with multiple observersasync-oneshot-response - Use oneshot channel for request-response patternsasync-joinset-structured - Use JoinSet for managing dynamic collections of spawned tasksasync-clone-before-await - Clone Arc/Rc data before await points to avoid holding references across suspensionasync-fn-in-trait - Use native async fn in traits (stable 1.75) instead of the async_trait macroasync-async-fn-bounds - Use AsyncFn/AsyncFnMut/AsyncFnOnce bounds instead of F: Fn() -> Fut, Fut: Futureasync-cancel-safety - Ensure futures used in tokio::select! branches are cancellation-safeconc-rayon-par-iter - Use rayon's par_iter() for CPU-bound data parallelismconc-scoped-threads - Use std::thread::scope to borrow stack data across threadsconc-atomic-ordering - Use the weakest correct memory Ordering for every atomic operationconc-thread-local - Prefer thread_local! with Cell/RefCell over static mutopt-inline-small - Use #[inline] for small hot functionsopt-inline-always-rare - Use #[inline(always)] sparingly—only for critical hot paths proven by profilingopt-inline-never-cold - Use #[inline(never)] and #[cold] for error paths and rarely-executed codeopt-cold-unlikely - Mark unlikely code paths with #[cold] to help compiler optimizationopt-likely-hint - Use code structure to hint at likely branches; use intrinsics on nightlyopt-lto-release - Enable LTO in release buildsopt-codegen-units - Set codegen-units = 1 for maximum optimization in release buildsopt-pgo-profile - Use Profile-Guided Optimization (PGO) for maximum performanceopt-target-cpu - Use target-cpu=native for maximum performance on known deployment targetsopt-bounds-check - Use iterators and patterns that eliminate bounds checks in hot pathsopt-simd-portable - Use portable SIMD for vectorized operations across architecturesopt-cache-friendly - Organize data for cache-efficient access patternsnum-overflow-explicit - Handle integer overflow explicitly: checked_/saturating_/wrapping_/overflowing_num-cast-try-from - Avoid as for narrowing casts; use From for widening and TryFrom for narrowingnum-float-compare - Don't compare floats with ==; use a tolerance, and total_cmp for orderingnum-saturating-clamp - Bound values with clamp and saturating arithmeticnum-nonzero - Use NonZero* types to forbid zero and unlock the niche optimizationtype-newtype-ids - Wrap IDs in newtypes: UserId(u64)type-newtype-validated - Use newtypes to enforce validation at construction timetype-enum-states - Use enums for mutually exclusive statestype-option-nullable - Use Option<T> for values that might not existtype-result-fallible - Use Result<T, E> for operations that can failtype-phantom-marker - Use PhantomData to express type relationships without runtime costtype-never-diverge - Use ! (never type) for functions that never returntype-generic-bounds - Add trait bounds only where needed, prefer where clauses for readabilitytype-no-stringly - Avoid stringly-typed APIs; use enums, newtypes, or validated typestype-repr-transparent - Use #[repr(transparent)] for newtypes in FFI contextstype-deref-coercion - Implement Deref/DerefMut only for smart-pointer and transparent wrapper typestype-display-vs-debug - Use Display for user-facing output and Debug for diagnostics; never swap themtype-numeric-fmt - Implement LowerHex, UpperHex, Octal, and Binary for numeric newtypestrait-associated-type-vs-generic - Use an associated type when each impl has exactly one output type; use a generic parameter when a type can implement the trait for many input typestrait-blanket-impl - Use a blanket impl impl<T: Bound> Trait for T to give behaviour to every type that satisfies a boundtrait-coherence-newtype - Respect the orphan rule; wrap a foreign type in a newtype to implement a foreign trait on ittrait-default-methods - Define a trait in terms of a few required methods plus defaulted ones built on top of themtrait-dyn-vs-generic - Choose static dispatch (generics / impl Trait) vs dynamic dispatch (dyn Trait) deliberatelytrait-object-safety - Keep a trait dyn-compatible (object-safe) when you need dyn Traitconv-tryfrom-fallible - Implement TryFrom for fallible conversions instead of ad-hoc conversion functionsconv-fromstr-parsing - Implement FromStr to enable str::parse for string-to-type conversionsconv-asmut-mutable - Accept impl AsMut<T> for flexible mutable borrowed inputs instead of concrete mutable referencesconst-block - Use inline const { } blocks for compile-time evaluation and assertionsconst-fn - Make functions const fn when they can run at compile timeconst-generics - Parameterize over values with const generics <const N: usize>const-vs-static - Use const for an inlined value and static for a single addressed instanceserde-rename-all - Match the external naming convention with #[serde(rename_all = ...)]serde-default-compat - Use #[serde(default)] for optional and backward-compatible fieldsserde-skip-empty - Omit empty fields with skip_serializing_ifserde-flatten - Inline nested structs or capture extra keys with #[serde(flatten)]serde-enum-representation - Choose enum tagging deliberately: externally, internally, adjacently tagged, or untaggedserde-deny-unknown-fields - Reject unexpected keys with #[serde(deny_unknown_fields)]serde-custom-with - Customize a field's (de)serialization with with / serialize_with / deserialize_withserde-try-from-validate - Validate while deserializing with #[serde(try_from = "Raw")]pat-let-else - Use let ... else for early-return pattern extractionpat-matches-macro - Use matches!() for boolean pattern testspat-if-let-chains - Use if let chains to combine pattern bindings and conditionspat-exhaustive-enum - Match owned enums exhaustively; avoid catch-all _ that hides new variantspat-at-bindings - Use @ bindings to capture a value while matching it against a patternmacro-prefer-functions - Reach for a macro only when a function or generic cannot express itmacro-rules-hygiene - Rely on macro_rules! hygiene and use $crate for paths to your crate's itemsmacro-fragment-specifiers - Capture with precise fragment specifiers, not raw :tt, where you canmacro-export-crate-path - Export declarative macros with #[macro_export] and a clean import pathmacro-private-helpers - Hide macro-generated helper items behind a #[doc(hidden)] pub mod __privatemacro-proc-two-crate - Put procedural macros in a dedicated proc-macro = true crate and re-export from the facademacro-proc-syn-quote - Build procedural macros with syn, quote, and proc-macro2macro-proc-error-spans - Report proc-macro errors as spanned compile errors, never by panickingclosure-fn-trait-bounds - Require the least restrictive Fn trait a callback needs (FnOnce ⊇ FnMut ⊇ Fn)closure-impl-fn-return - Return closures as impl Fn/FnMut/FnOnce, not Box<dyn Fn>closure-move-capture - Use move for closures that outlive the current scope; clone before move to keep the originalclosure-static-vs-dyn - Accept impl Fn (generic) for hot callbacks; use &dyn Fn/Box<dyn Fn> to cut code size or to store themclosure-disjoint-capture - Capture only what you use; lean on edition-2021 disjoint closure capturescoll-binaryheap - Use BinaryHeap for a priority queue or repeated max-extractioncoll-map-choice - Pick the map by access pattern: HashMap (fast, unordered), BTreeMap (sorted / range queries), IndexMap (insertion order)coll-seq-choice - Default to Vec; use VecDeque for queue/deque behaviour; avoid LinkedListcoll-set-membership - Use HashSet/BTreeSet for membership tests and dedup, not linear Vec::containsname-types-camel - Use UpperCamelCase for types, traits, and enum namesname-variants-camel - Use UpperCamelCase for enum variantsname-funcs-snake - Use snake_case for functions, methods, variables, and modulesname-consts-screaming - Use SCREAMING_SNAKE_CASE for constants and staticsname-lifetime-short - Use short, conventional lifetime names: 'a, 'b, 'de, 'srcname-type-param-single - Use single uppercase letters for type parameters: T, E, K, Vname-as-free - as_ prefix: free reference conversionname-to-expensive - Use to_ prefix for expensive conversions that allocate or computename-into-ownership - Use into_ prefix for ownership-consuming conversionsname-no-get-prefix - Omit get_ prefix for simple gettersname-is-has-bool - Use is_, has_, can_, should_ prefixes for boolean-returning methodsname-iter-convention - Use iter/iter_mut/into_iter for iterator methodsname-iter-method - Name iterator methods iter(), iter_mut(), and into_iter() consistentlyname-iter-type-match - Name iterator types after their source methodname-acronym-word - Treat acronyms as words in identifiers: HttpServer, not HTTPServername-crate-no-rs - Don't suffix crate names with -rs or -rusttest-cfg-test-module - Put unit tests in #[cfg(test)] mod tests { } within each moduletest-use-super - Use use super::*; in test modules to access parent module itemstest-integration-dir - Put integration tests in the tests/ directorytest-descriptive-names - Use descriptive test names that explain what is being testedtest-arrange-act-assert - Structure tests with clear Arrange, Act, Assert sectionstest-proptest-properties - Use proptest for property-based testingtest-mockall-mocking - Use mockall for trait mockingtest-mock-traits - Use traits for dependencies to enable mocking in teststest-fixture-raii - Use RAII pattern (Drop trait) for automatic test cleanuptest-tokio-async - Use #[tokio::test] for async teststest-should-panic - Use #[should_panic] to test that code panics as expectedtest-criterion-bench - Use criterion for benchmarkingtest-doctest-examples - Keep documentation examples as executable docteststest-loom-concurrency - Use loom to exhaustively test lock-free and concurrent codetest-snapshot-testing - Use snapshot testing (insta) for complex or serialized outputdoc-all-public - Document all public items with /// doc commentsdoc-module-inner - Use //! for module-level documentationdoc-examples-section - Include # Examples with runnable codedoc-errors-section - Include # Errors section for fallible functionsdoc-panics-section - Include # Panics section for functions that can panicdoc-safety-section - Include # Safety section 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 to reference types and itemsdoc-link-types - Use intra-doc links to connect related types and functionsdoc-cargo-metadata - Fill Cargo.toml metadata for published cratesdoc-crate-readme - Unify the README and crate root docs with #![doc = include_str!("../README.md")]obs-tracing-over-log - Use tracing for structured, span-aware diagnostics instead of println! or bare logobs-library-facade - Libraries emit through the tracing/log facade and never install a subscriberobs-structured-fields - Record structured key-value fields, not values interpolated into the message stringobs-instrument-spans - Use #[tracing::instrument] and spans to attach context to async tasks and requestsobs-levels-filter - Use log levels meaningfully and filter with EnvFilter / RUST_LOGobs-error-chain - Log errors with their full source chain, and log each error exactly onceobs-no-sensitive-data - Never log secrets or PII; redact or skip themperf-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 optimizingperf-ahash - Use a faster hasher (ahash / FxHashMap) when DoS resistance is not neededperf-io-buffering - Wrap Read/Write in BufReader/BufWriter for many small operationsproj-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 inheritance for consistent versions across cratesproj-feature-additive - Design Cargo features to be strictly additiveproj-msrv-declare - Declare rust-version (MSRV) in Cargo.toml and test it in CIproj-build-rs-minimal - Keep build.rs minimal, deterministic, and idempotentlint-deny-correctness - #![deny(clippy::correctness)]lint-warn-suspicious - Enable clippy::suspicious for likely bugslint-warn-style - Enable clippy::style for idiomatic codelint-warn-complexity - Enable clippy::complexity for simpler codelint-warn-perf - Enable clippy::perf for performance improvementslint-pedantic-selective - Enable clippy::pedantic selectivelylint-missing-docs - Warn on missing documentation for public itemslint-unsafe-doc - Require documentation for unsafe blockslint-cargo-metadata - Enable clippy::cargo for published crateslint-rustfmt-check - Run cargo fmt --check in CIlint-workspace-lints - Configure lints at workspace level for consistent enforcementlint-cfg-check - Enable unexpected_cfgs and declare known cfgs to catch feature-gate typoslint-clippy-nursery-selected - Enable high-value clippy::nursery lints selectively, not the whole groupanti-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 await pointsanti-string-for-str - Don't accept &String when &str worksanti-vec-for-slice - Don't accept &Vec when &[T] worksanti-index-over-iter - Don't use indexing when iterators workanti-panic-expected - Don't panic on expected or recoverable errorsanti-empty-catch - Don't silently ignore errorsanti-over-abstraction - Don't over-abstract with excessive genericsanti-premature-optimize - Don't optimize before profilinganti-type-erasure - Don't use Box 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 where enums or newtypes would provide type safety[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-, pat- |
| New struct/API | api-, type-, conv-, doc- |
| Async code | async-, own- |
| Concurrency / parallelism | conc-, async-, own- |
| Unsafe code | unsafe-, type-, test- |
| Error handling | err-, api-, pat- |
| Type conversions | conv-, api- |
| Serialization (serde) | serde-, type-, api- |
| Numeric / arithmetic | num-, type- |
| Macros / code generation | macro-, anti- |
| Closures / callbacks | closure-, type- |
| Logging / observability | obs-, err- |
| Memory optimization | mem-, own-, perf- |
| Performance tuning | opt-, mem-, perf- |
| Code review | anti-, lint- |
This skill is an independent synthesis of official Rust guidance, well-known books, and patterns from widely-used crates. It is not affiliated with or endorsed by the Rust project or any crate author; the text and code examples are original.
Official Rust documentation
Books & guides
Tooling
Real-world codebases studied for idioms
This project is MIT-licensed. Referenced upstream materials remain under their own licenses (the official Rust docs and API Guidelines are dual MIT / Apache-2.0).
Last scanned: 5/30/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-05-30T15:31:57.522Z",
"npmAuditRan": true,
"pipAuditRan": true
}
265 Rust rules your AI coding agent can use to write better code. Current for Rust 1.96 (2024 edition).
Works with Claude Code, Cursor, Windsurf, Copilot, Codex, Aider, Zed, Amp, Cline, and pretty much any other agent that supports skills.
Out of the box, coding agents write average Rust — they clone to dodge the borrow checker, .unwrap() everything, and reach for Box<dyn Trait> when impl Trait would do. These rules encode what expert Rust actually looks like: idiomatic, fast, and safe. Each rule is small and focused, so the agent pulls in only what's relevant to the code in front of it.
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
/rust-skills is this unsafe block sound?
The agent loads the relevant rules and applies them to your code.
Ask the agent to review a function like this:
// before
fn first_word(s: &String) -> String {
s.clone().split_whitespace().next().unwrap().to_string()
}
With these rules loaded, it knows to take &str instead of &String, drop the
needless clone() and allocation, and return an Option instead of panicking:
// after — applies own-slice-over-vec, own-borrow-over-clone, anti-unwrap-abuse
fn first_word(s: &str) -> Option<&str> {
s.split_whitespace().next()
}
265 rules split into 26 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 | 17 | SmallVec, arenas, avoiding allocations, mem::take, drop order |
| Unsafe Code | 7 | SAFETY: comments, Miri, MaybeUninit, 2024-edition unsafe |
| API Design | 17 | Builder pattern, newtypes, sealed traits, FromIterator |
| Async | 18 | Tokio patterns, channels, async fn in traits, cancel safety |
| Concurrency | 4 | rayon, scoped threads, atomic ordering, thread-locals |
| Optimization | 12 | LTO, inlining, PGO, SIMD |
| Numeric & Arithmetic | 5 | Overflow handling, as vs TryFrom, float compare, NonZero |
| Type Safety | 13 | Newtypes, parse don't validate, Deref, Display/Debug |
| Trait & Generics Design | 6 | dyn vs generic, associated types, blanket impls, object safety, orphan rule |
| Conversions | 3 | TryFrom, FromStr, AsMut |
| Const & Compile-Time | 4 | const fn, const vs static, const generics, const {} blocks |
| Serde | 8 | rename_all, default, flatten, enum tagging, validate-on-deserialize |
| Pattern Matching | 5 | let-else, matches!, if-let chains, exhaustive matches |
| Macros | 8 | macro_rules! hygiene, fragment specifiers, proc-macros with syn/quote |
| Closures | 5 | Fn/FnMut/FnOnce bounds, returning impl Fn, move & disjoint capture |
| Collections | 4 | HashMap/BTreeMap/IndexMap, Vec/VecDeque, sets, BinaryHeap |
| Naming | 16 | Following Rust API Guidelines |
| Testing | 15 | Proptest, mockall, criterion, loom, snapshot tests |
| Docs | 12 | Doc examples, intra-doc links, README/crate-doc unification |
| Observability | 7 | tracing over log, spans, structured fields, redacting secrets |
| Performance | 13 | Iterators, entry API, faster hashers, I/O buffering |
| Project Structure | 14 | Workspaces, module layout, features, MSRV |
| Linting | 13 | Clippy config, CI setup, unexpected_cfgs |
| Anti-patterns | 15 | Common mistakes and how to fix them |
Each rule has:
The design is built for low token cost and easy auditing:
SKILL.md is a lightweight index — every rule listed as a one-line summary, grouped by category, with a link to its file. The agent reads this first.rules/ holds one Markdown file per rule (<prefix>-<name>.md). The agent opens only the handful relevant to your code instead of loading all 218 — progressive disclosure keeps context small.own-, err-, unsafe-, async-, …) map directly to categories, so an agent reviewing async code can pull just async-, conc-, and own- rules.CLAUDE.md and AGENTS.md are symlinks to SKILL.md, so the same content works across agent conventions.
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 -o AGENTS.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
mkdir -p .github
curl -o .github/copilot-instructions.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
Add to .aider.conf.yml:
read: path/to/rust-skills/SKILL.md
Or pass it directly:
aider --read path/to/rust-skills/SKILL.md
curl -o AGENTS.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
git clone https://github.com/leonardomso/rust-skills.git .agents/skills/rust-skills
mkdir -p .clinerules
curl -o .clinerules/rust-skills.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
If your agent supports the AGENTS.md standard:
curl -o AGENTS.md https://raw.githubusercontent.com/leonardomso/rust-skills/master/SKILL.md
See SKILL.md for the full list with links to each rule file.
These rules are an independent synthesis of official Rust guidance, well-known books, and patterns drawn from widely-used open-source crates. They are not affiliated with or endorsed by the Rust project or any crate author. The text and code examples are original summaries — no substantial content is copied from the sources below.
Official Rust documentation
Books & guides
Tooling
Real-world codebases studied for idioms
This project is MIT-licensed. Referenced upstream materials remain under their own licenses — the official Rust documentation and API Guidelines are dual MIT / Apache-2.0.
PRs welcome. To add or change a rule:
rules/<prefix>-<name>.md using a kebab-case id with an existing category prefix (own-, err-, mem-, …).> one-line summary, then ## Why It Matters, ## Bad, ## Good, and ## See Also (with links that resolve).SKILL.md (Quick Reference list + the category count) so it stays in sync.# prefix-rule-name
> One-line imperative summary.
## Why It Matters
Two to four sentences.
## Bad
```rust
// th
rust-skills is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by leonardomso. A collection of 265 rules across 26 categories that AI coding agents can use to write idiomatic, fast, and safe Rust. It has 274 GitHub stars.
Yes. rust-skills passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/leonardomso/rust-skills" and add it to your Claude Code skills directory (see the Installation section above). rust-skills ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
rust-skills is primarily written in Python. It is open-source under leonardomso on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh rust-skills against similar tools.
No comments yet. Be the first to share your thoughts!