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- UseCow<'a, T>for conditional ownership - •
own-arc-shared- UseArc<T>for thread-safe shared ownership - •
own-rc-single-thread- UseRc<T>for single-threaded sharing - •
own-refcell-interior- UseRefCell<T>for interior mutability (single-thread) - •
own-mutex-interior- UseMutex<T>for interior mutability (multi-thread) - •
own-rwlock-readers- UseRwLock<T>when reads dominate writes - •
own-copy-small- DeriveCopyfor small, trivial types - •
own-clone-explicit- MakeCloneexplicit, 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- Usethiserrorfor library error types - •
err-anyhow-app- Useanyhowfor application error handling - •
err-result-over-panic- ReturnResult, 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, notBox<dyn Error>
3. Memory Optimization (CRITICAL)
- •
mem-with-capacity- Usewith_capacity()when size is known - •
mem-smallvec- UseSmallVecfor usually-small collections - •
mem-arrayvec- UseArrayVecfor bounded-size collections - •
mem-box-large-variant- Box large enum variants to reduce type size - •
mem-boxed-slice- UseBox<[T]>instead ofVec<T>when fixed - •
mem-thinvec- UseThinVecfor often-empty vectors - •
mem-clone-from- Useclone_from()to reuse allocations - •
mem-reuse-collections- Reuse collections withclear()in loops - •
mem-avoid-format- Avoidformat!()when string literals work - •
mem-write-over-format- Usewrite!()instead offormat!() - •
mem-arena-allocator- Use arena allocators for batch allocations - •
mem-zero-copy- Use zero-copy patterns with slices andBytes - •
mem-compact-string- UseCompactStringfor 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- Acceptimpl Into<T>for flexible string inputs - •
api-impl-asref- Acceptimpl 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- ImplementFrom, notInto(auto-derived) - •
api-default-impl- ImplementDefaultfor sensible defaults - •
api-common-traits- ImplementDebug,Clone,PartialEqeagerly - •
api-serde-optional- GateSerialize/Deserializebehind feature flag
5. Async/Await (HIGH)
- •
async-tokio-runtime- Use Tokio for production async runtime - •
async-no-lock-await- Never holdMutex/RwLockacross.await - •
async-spawn-blocking- Usespawn_blockingfor CPU-intensive work - •
async-tokio-fs- Usetokio::fsnotstd::fsin async code - •
async-cancellation-token- UseCancellationTokenfor graceful shutdown - •
async-join-parallel- Usetokio::join!for parallel operations - •
async-try-join- Usetokio::try_join!for fallible parallel ops - •
async-select-racing- Usetokio::select!for racing/timeouts - •
async-bounded-channel- Use bounded channels for backpressure - •
async-mpsc-queue- Usempscfor work queues - •
async-broadcast-pubsub- Usebroadcastfor pub/sub patterns - •
async-watch-latest- Usewatchfor latest-value sharing - •
async-oneshot-response- Useoneshotfor request/response - •
async-joinset-structured- UseJoinSetfor 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- Uselikely()/unlikely()for branch hints - •
opt-lto-release- Enable LTO in release builds - •
opt-codegen-units- Usecodegen-units = 1for max optimization - •
opt-pgo-profile- Use PGO for production builds - •
opt-target-cpu- Settarget-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- UseUpperCamelCasefor types, traits, enums - •
name-variants-camel- UseUpperCamelCasefor enum variants - •
name-funcs-snake- Usesnake_casefor functions, methods, modules - •
name-consts-screaming- UseSCREAMING_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- Noget_prefix for simple getters - •
name-is-has-bool- Useis_,has_,can_for boolean methods - •
name-iter-convention- Useiter/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- UseOption<T>for nullable values - •
type-result-fallible- UseResult<T, E>for fallible operations - •
type-phantom-marker- UsePhantomData<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- Useuse super::*;in test modules - •
test-integration-dir- Put integration tests intests/directory - •
test-descriptive-names- Use descriptive test names - •
test-arrange-act-assert- Structure tests as arrange/act/assert - •
test-proptest-properties- Useproptestfor property-based testing - •
test-mockall-mocking- Usemockallfor 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- Usecriterionfor 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- FillCargo.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'tcollect()intermediate iterators - •
perf-entry-api- Useentry()API for map insert-or-update - •
perf-drain-reuse- Usedrain()to reuse allocations - •
perf-extend-batch- Useextend()for batch insertions - •
perf-chain-avoid- Avoidchain()in hot loops - •
perf-collect-into- Usecollect_into()for reusing containers - •
perf-black-box-bench- Useblack_box()in benchmarks - •
perf-release-profile- Optimize release profile settings - •
perf-profile-first- Profile before optimizing
12. Project Structure (LOW)
- •
proj-lib-main-split- Keepmain.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- Usemod.rsfor multi-file modules - •
proj-pub-crate-internal- Usepub(crate)for internal APIs - •
proj-pub-super-parent- Usepub(super)for parent-only visibility - •
proj-pub-use-reexport- Usepub usefor clean public API - •
proj-prelude-module- Createpreludemodule for common imports - •
proj-bin-dir- Put multiple binaries insrc/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- Enableclippy::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- Runcargo 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 emptyif 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 useBox<dyn Trait>whenimpl Traitworks - •
anti-format-hot-path- Don't useformat!()in hot paths - •
anti-collect-intermediate- Don'tcollect()intermediate iterators - •
anti-stringly-typed- Don't use strings for structured data
Recommended Cargo.toml Settings
Standalone project
toml
[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
Workspace
toml
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.package]
edition = "2024"
rust-version = "1.85"
license = "MIT"
[workspace.dependencies]
# Pin shared dependencies here
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
thiserror = "2"
anyhow = "1"
[workspace.lints.rust]
unsafe_code = "forbid"
[workspace.lints.clippy]
correctness = { level = "deny", priority = -1 }
suspicious = { level = "warn", priority = -1 }
style = { level = "warn", priority = -1 }
complexity = { level = "warn", priority = -1 }
perf = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
[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
Member crates inherit from the workspace:
toml
[package]
name = "my-crate"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
tokio = { workspace = true }
serde = { workspace = true }
[lints]
workspace = true
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:
- •Rust API Guidelines
- •Rust Performance Book
- •Rust Design Patterns
- •Production codebases: ripgrep, tokio, serde, polars, axum, deno
- •Clippy lint documentation
- •Community conventions (2024-2025)