Idiomatic Rust patterns, ownership, error handling, traits, concurrency, and best practices for building safe, performant applications. Use when this capability is needed.
Idiomatic Rust patterns, ownership, error handling, traits, concurrency, and best practices for building safe, performant applications. Use when this capability is needed.
metadata
{"author":"affaan-m"}
Rust Development Patterns
Idiomatic Rust patterns and best practices for building safe, performant, and maintainable applications.
When to Use
Writing new Rust code
Reviewing Rust code
Refactoring existing Rust code
Designing crate structure and module layout
How It Works
This skill enforces idiomatic Rust conventions across six key areas: ownership and borrowing to prevent data races at compile time, Result/? error propagation with thiserror for libraries and anyhow for applications, enums and exhaustive pattern matching to make illegal states unrepresentable, traits and generics for zero-cost abstraction, safe concurrency via Arc<Mutex<T>>, channels, and async/await, and minimal pub surfaces organized by domain.
Core Principles
1. Ownership and Borrowing
Rust's ownership system prevents data races and memory bugs at compile time.
// Good: Pass references when you don't need ownershipfnprocess(data: &[u8]) ->usize {
data.len()
}
(data: <>) Record {
Record { payload: data }
}
(data: &<>) {
= data.();
cloned.()
}
// Good: Take ownership only when you need to store or consume
fn
store
Vec
u8
->
// Bad: Cloning unnecessarily to avoid borrow checker
fn
process_bad
Vec
u8
->
usize
let
cloned
clone
// Wasteful — just borrow
len
Use Cow for Flexible Ownership
use std::borrow::Cow;
fnnormalize(input: &str) -> Cow<'_, str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_"))
} else {
Cow::Borrowed(input) // Zero-cost when no mutation needed
}
}
Error Handling
Use Result and ? — Never unwrap() in Production
// Good: Propagate errors with contextuse anyhow::{Context, Result};
fnload_config(path: &str) ->Result<Config> {
letcontent = std::fs::read_to_string(path)
.with_context(|| format!("failed to read config from {path}"))?;
letconfig: Config = toml::from_str(&content)
.with_context(|| format!("failed to parse config from {path}"))?;
Ok(config)
}
// Bad: Panics on errorfnload_config_bad(path: &str) -> Config {
letcontent = std::fs::read_to_string(path).unwrap(); // Panics!
toml::from_str(&content).unwrap()
}
Library Errors with thiserror, Application Errors with anyhow
// Use when you need heterogeneous collections or plugin systemstraitHandler: Send + Sync {
fnhandle(&self, request: &Request) -> Response;
}
structRouter {
handlers: Vec<Box<dyn Handler>>,
}
// Use generics when you need performance (monomorphization)fnfast_process<H: Handler>(handler: &H, request: &Request) -> Response {
handler.handle(request)
}
Newtype Pattern for Type Safety
// Good: Distinct types prevent mixing up argumentsstructUserId(u64);
structOrderId(u64);
fnget_order(user: UserId, order: OrderId) ->Result<Order> {
// Can't accidentally swap user and order IDs
todo!()
}
// Bad: Easy to swap argumentsfnget_order_bad(user_id: u64, order_id: u64) ->Result<Order> {
todo!()
}
// Acceptable: FFI boundary with documented invariants/// # Safety/// `ptr` must be a valid, aligned pointer to an initialized `Widget`.unsafefnwidget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
// SAFETY: caller guarantees ptr is valid and alignedunsafe { &*ptr }
}
// Acceptable: Performance-critical path with proof of correctness// SAFETY: index is always < len due to the loop boundunsafe { slice.get_unchecked(index) }
When Unsafe Is NOT Acceptable
// Bad: Using unsafe to bypass borrow checker// Bad: Using unsafe for convenience// Bad: Using unsafe without a Safety comment// Bad: Transmuting between unrelated types
// Good: pub(crate) for internal sharingpub(crate) fnvalidate_input(input: &str) ->bool {
!input.is_empty()
}
// Good: Re-export public API from lib.rspubmod auth;
pubuse auth::AuthMiddleware;
// Bad: Making everything pubpubfninternal_helper() {} // Should be pub(crate) or private
Tooling Integration
Essential Commands
# Build and check
cargo build
cargo check # Fast type checking without codegen
cargo clippy # Lints and suggestions
cargo fmt# Format code# Testing
cargo test
cargo test -- --nocapture # Show println output
cargo test --lib # Unit tests only
cargo test --test integration # Integration tests only# Dependencies
cargo audit # Security audit
cargo tree # Dependency tree
cargo update # Update dependencies# Performance
cargo bench # Run benchmarks
Quick Reference: Rust Idioms
Idiom
Description
Borrow, don't clone
Pass &T instead of cloning unless ownership is needed
Make illegal states unrepresentable
Use enums to model valid states only
? over unwrap()
Propagate errors, never panic in library/production code
Parse, don't validate
Convert unstructured data to typed structs at the boundary
Newtype for type safety
Wrap primitives in newtypes to prevent argument swaps
Prefer iterators over loops
Declarative chains are clearer and often faster
#[must_use] on Results
Ensure callers handle return values
Cow for flexible ownership
Avoid allocations when borrowing suffices
Exhaustive matching
No wildcard _ for business-critical enums
Minimal pub surface
Use pub(crate) for internal APIs
Anti-Patterns to Avoid
// Bad: .unwrap() in production codeletvalue = map.get("key").unwrap();
// Bad: .clone() to satisfy borrow checker without understanding whyletdata = expensive_data.clone();
process(&original, &data);
// Bad: Using String when &str sufficesfngreet(name: String) { /* should be &str */ }
// Bad: Box<dyn Error> in libraries (use thiserror instead)fnparse(input: &str) ->Result<Data, Box<dyn std::error::Error>> { todo!() }
// Bad: Ignoring must_use warningslet_ = validate(input); // Silently discarding a Result// Bad: Blocking in async contextasyncfnbad_async() {
std::thread::sleep(Duration::from_secs(1)); // Blocks the executor!// Use: tokio::time::sleep(Duration::from_secs(1)).await;
}
Remember: If it compiles, it's probably correct — but only if you avoid unwrap(), minimize unsafe, and let the type system work for you.