SKILL.md
Rust Best Practices
Comprehensive 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.
When to Apply
Reference these guidelines when:
- Writing new Rust functions, structs, or modules
- Implementing error handling or async code
- Designing public APIs for libraries
- Reviewing code for ownership/borrowing issues
- Optimizing memory usage or reducing allocations
- Tuning performance for hot paths
- Refactoring existing Rust code
Rule Categories by Priority
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
Quick Reference
1. Ownership & Borrowing (CRITICAL)
- own-borrow-over-clone - Prefer
&Tborrowing over.clone()
- own-slice-over-vec - Accept
&[T]not&Vec<T>,&strnot&String
- own-cow-conditional - Use
Cow<'a, T>for conditional ownership
- own-arc-shared - Use
Arc<T>for thread-safe shared ownership
- own-rc-single-thread - Use
Rc<T>for single-threaded sharing
- own-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 writes
- own-copy-small - Derive
Copyfor small, trivial types
- own-clone-explicit - Make
Cloneexplicit, avoid implicit copies
- own-move-large - Move large data instead of cloning
- own-lifetime-elision - Rely on lifetime elision when possible
2. Error Handling (CRITICAL)
- err-thiserror-lib - Use
thiserrorfor library error types
- err-anyhow-app - Use
anyhowfor application error handling
- err-result-over-panic - Return
Result, don't panic on expected errors
- err-context-chain - Add context with
.context()or.with_context()
- err-no-unwrap-prod - Never use
.unwrap()in production code
- err-expect-bugs-only - Use
.expect()only for programming errors
- err-question-mark - Use
?operator for clean propagation
- err-from-impl - Use
#[from]for automatic error conversion
- err-source-chain - Use
#[source]to chain underlying errors
- err-lowercase-msg - Error messages: lowercase, no trailing punctuation
- err-doc-errors - Document errors with
# Errorssection
- err-custom-type - Create custom error types, not
Box<dyn Error>
3. Memory Optimization (CRITICAL)
- mem-with-capacity - Use
with_capacity()when size is known
- mem-smallvec - Use
SmallVecfor usually-small collections
- mem-arrayvec - Use
ArrayVecfor bounded-size collections
- mem-box-large-variant - Box large enum variants to reduce type size
- mem-boxed-slice - Use
Box<[T]>instead ofVec<T>when fixed
- mem-thinvec - Use
ThinVecfor often-empty vectors
- mem-clone-from - Use
clone_from()to reuse allocations
- mem-reuse-collections - Reuse collections with
clear()in loops
- mem-avoid-format - Avoid
format!()when string literals work
- mem-write-over-format - Use
write!()instead offormat!()
- mem-arena-allocator - Use arena allocators for batch allocations
- mem-zero-copy - Use zero-copy patterns with slices and
Bytes
- mem-compact-string - Use
CompactStringfor small string optimization
- mem-smaller-integers - Use smallest integer type that fits
- mem-assert-type-size - Assert hot type sizes to prevent regressions
4. API Design (HIGH)
- api-builder-pattern - Use Builder pattern for complex construction
- api-builder-must-use - Add
#[must_use]to builder types
- api-newtype-safety - Use newtypes for type-safe distinctions
- api-typestate - Use typestate for compile-time state machines
- api-sealed-trait - Seal traits to prevent external implementations
- api-extension-trait - Use extension traits to add methods to foreign types
- api-parse-dont-validate - Parse into validated types at boundaries
- api-impl-into - Accept
impl Into<T>for flexible string inputs
- api-impl-asref - Accept
impl AsRef<T>for borrowed inputs
- api-must-use - Add
#[must_use]toResultreturning functions
- api-non-exhaustive - Use
#[non_exhaustive]for future-proof enums/structs
- api-from-not-into - Implement
From, notInto(auto-derived)
- api-default-impl - Implement
Defaultfor sensible defaults
- api-common-traits - Implement
Debug,Clone,PartialEqeagerly
- api-serde-optional - Gate
Serialize/Deserializebehind feature flag
5. Async/Await (HIGH)
- async-tokio-runtime - Use Tokio for production async runtime
- async-no-lock-await - Never hold
Mutex/RwLockacross.await
- async-spawn-blocking - Use
spawn_blockingfor CPU-intensive work
- async-tokio-fs - Use
tokio::fsnotstd::fsin async code
- async-cancellation-token - Use
CancellationTokenfor graceful shutdown
- async-join-parallel - Use
tokio::join!for parallel operations
- async-try-join - Use
tokio::try_join!for fallible parallel ops
- async-select-racing - Use
tokio::select!for racing/timeouts
- async-bounded-channel - Use bounded channels for backpressure
- async-mpsc-queue - Use
mpscfor work queues
- async-broadcast-pubsub - Use
broadcastfor pub/sub patterns
- async-watch-latest - Use
watchfor latest-value sharing
- async-oneshot-response - Use
oneshotfor request/response
- async-joinset-structured - Use
JoinSetfor dynamic task groups
- async-clone-before-await - Clone data before await, release locks
6. Compiler Optimization (HIGH)
- opt-inline-small - Use
#[inline]for small hot functions
- opt-inline-always-rare - Use
#[inline(always)]sparingly
- opt-inline-never-cold - Use
#[inline(never)]for cold paths
- opt-cold-unlikely - Use
#[cold]for error/unlikely paths
- opt-likely-hint - Use
likely()/unlikely()for branch hints
- opt-lto-release - Enable LTO in release builds
- opt-codegen-units - Use
codegen-units = 1for max optimization
- opt-pgo-profile - Use PGO for production builds
- opt-target-cpu - Set
target-cpu=nativefor local builds
- opt-bounds-check - Use iterators to avoid bounds checks
- opt-simd-portable - Use portable SIMD for data-parallel ops
- opt-cache-friendly - Design cache-friendly data layouts (SoA)
7. Naming Conventions (MEDIUM)
- name-types-camel - Use
UpperCamelCasefor types, traits, enums
- name-variants-camel - Use
UpperCamelCasefor enum variants
- name-funcs-snake - Use
snake_casefor functions, methods, modules
- name-consts-screaming - Use
SCREAMING_SNAKE_CASEfor constants/statics
- name-lifetime-short - Use short lowercase lifetimes:
'a,'de,'src
- name-type-param-single - Use single uppercase for type params:
T,E,K,V
- name-as-free -
as_prefix: free reference conversion
- name-to-expensive -
to_prefix: expensive conversion
- name-into-ownership -
into_prefix: ownership transfer
- name-no-get-prefix - No
get_prefix for simple getters
- name-is-has-bool - Use
is_,has_,can_for boolean methods
- name-iter-convention - Use
iter/iter_mut/into_iterfor iterators
- name-iter-method - Name iterator methods consistently
- name-iter-type-match - Iterator type names match method
- name-acronym-word - Treat acronyms as words:
UuidnotUUID
- name-crate-no-rs - Crate names: no
-rssuffix
8. Type Safety (MEDIUM)
- type-newtype-ids - Wrap IDs in newtypes:
UserId(u64)
- type-newtype-validated - Newtypes for validated data:
Email,Url
- type-enum-states - Use enums for mutually exclusive states
- type-option-nullable - Use
Option<T>for nullable values
- type-result-fallible - Use
Result<T, E>for fallible operations
- type-phantom-marker - Use
PhantomData<T>for type-level markers
- type-never-diverge - Use
!type for functions that never return
- type-generic-bounds - Add trait bounds only where needed
- type-no-stringly - Avoid stringly-typed APIs, use enums/newtypes
- type-repr-transparent - Use
#[repr(transparent)]for FFI newtypes
9. Testing (MEDIUM)
- test-cfg-test-module - Use
#[cfg(test)] mod tests { }
- test-use-super - Use
use super::*;in test modules
- test-integration-dir - Put integration tests in
tests/directory
- test-descriptive-names - Use descriptive test names
- test-arrange-act-assert - Structure tests as arrange/act/assert
- test-proptest-properties - Use
proptestfor property-based testing
- test-mockall-mocking - Use
mockallfor trait mocking
- test-mock-traits - Use traits for dependencies to enable mocking
- test-fixture-raii - Use RAII pattern (Drop) for test cleanup
- test-tokio-async - Use
#[tokio::test]for async tests
- test-should-panic - Use
#[should_panic]for panic tests
- test-criterion-bench - Use
criterionfor benchmarking
- test-doctest-examples - Keep doc examples as executable tests
10. Documentation (MEDIUM)
- doc-all-public - Document all public items with
///
- doc-module-inner - Use
//!for module-level documentation
- doc-examples-section - Include
# Exampleswith runnable code
- doc-errors-section - Include
# Errorsfor fallible functions
- doc-panics-section - Include
# Panicsfor panicking functions
- doc-safety-section - Include
# Safetyfor unsafe functions
- doc-question-mark - Use
?in examples, not.unwrap()
- doc-hidden-setup - Use
#prefix to hide example setup code
- doc-intra-links - Use intra-doc links:
[Vec]
- doc-link-types - Link related types and functions in docs
- doc-cargo-metadata - Fill
Cargo.tomlmetadata
11. Performance Patterns (MEDIUM)
- perf-iter-over-index - Prefer iterators over manual indexing
- perf-iter-lazy - Keep iterators lazy, collect() only when needed
- perf-collect-once - Don't
collect()intermediate iterators
- perf-entry-api - Use
entry()API for map insert-or-update
- perf-drain-reuse - Use
drain()to reuse allocations
- perf-extend-batch - Use
extend()for batch insertions
- perf-chain-avoid - Avoid
chain()in hot loops
- perf-collect-into - Use
collect_into()for reusing containers
- perf-black-box-bench - Use
black_box()in benchmarks
- perf-release-profile - Optimize release profile settings
- perf-profile-first - Profile before optimizing
12. Project Structure (LOW)
- proj-lib-main-split - Keep
main.rsminimal, logic inlib.rs
- proj-mod-by-feature - Organize modules by feature, not type
- proj-flat-small - Keep small projects flat
- proj-mod-rs-dir - Use
mod.rsfor multi-file modules
- proj-pub-crate-internal - Use
pub(crate)for internal APIs
- proj-pub-super-parent - Use
pub(super)for parent-only visibility
- proj-pub-use-reexport - Use
pub usefor clean public API
- proj-prelude-module - Create
preludemodule for common imports
- proj-bin-dir - Put multiple binaries in
src/bin/
- proj-workspace-large - Use workspaces for large projects
- proj-workspace-deps - Use workspace dependency inheritance
13. Clippy & Linting (LOW)
- lint-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::pedanticselectively
- lint-missing-docs -
#![warn(missing_docs)]
- lint-unsafe-doc -
#![warn(clippy::undocumented_unsafe_blocks)]
- lint-cargo-metadata -
#![warn(clippy::cargo)]for published crates
- lint-rustfmt-check - Run
cargo fmt --checkin CI
- lint-workspace-lints - Configure lints at workspace level
14. Anti-patterns (REFERENCE)
- anti-unwrap-abuse - Don't use
.unwrap()in production code
- anti-expect-lazy - Don't use
.expect()for recoverable errors
- anti-clone-excessive - Don't clone when borrowing works
- anti-lock-across-await - Don't hold locks across
.await
- anti-string-for-str - Don't accept
&Stringwhen&strworks
- anti-vec-for-slice - Don't accept
&Vec<T>when&[T]works
- anti-index-over-iter - Don't use indexing when iterators work
- anti-panic-expected - Don't panic on expected/recoverable errors
- anti-empty-catch - Don't use empty
if let Err(_) = ...blocks
- anti-over-abstraction - Don't over-abstract with excessive generics
- anti-premature-optimize - Don't optimize before profiling
- anti-type-erasure - Don't use
Box<dyn Trait>whenimpl Traitworks
- anti-format-hot-path - Don't use
format!()in hot paths
- anti-collect-intermediate - Don't
collect()intermediate iterators
- anti-stringly-typed - Don't use strings for structured data
Recommended Cargo.toml Settings
[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
How to Use
This skill provides rule identifiers for quick reference. When generating or reviewing Rust code:
- Check relevant category based on task type
- Apply rules with matching prefix
- Prioritize CRITICAL > HIGH > MEDIUM > LOW
- Read rule files in
rules/for detailed examples
Rule Application by Task
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-
Sources
This skill synthesizes best practices from:
- Production codebases: ripgrep, tokio, serde, polars, axum, deno
- Clippy lint documentation
- Community conventions (2024-2025)