Rust coding standards with ownership patterns, type safety, clean code, and performance-conscious design. Use when creating or editing Rust files (*.rs), reviewing Rust code, or making any decisions in Rust projects. This skill applies to ALL Rust development — new files, modifications, refactoring, debugging, or code review.
Rust coding standards with ownership patterns, type safety, clean code, and performance-conscious design. Use when creating or editing Rust files (*.rs), reviewing Rust code, or making any decisions in Rust projects. This skill applies to ALL Rust development — new files, modifications, refactoring, debugging, or code review.
Write code that is correct first, clear second, and fast third. Leverage Rust's type system
and ownership model to catch bugs at compile time rather than runtime. The compiler is your
most powerful reviewer — design code so it rejects invalid states before they exist.
Ownership & Borrowing
The borrow checker enforces memory safety without a garbage collector. Work with it, not
around it.
Borrow before clone. If a function only reads data, take &T not T. Every .clone()
is a code smell — justify each one.
Own when storing. Structs that persist data should own it. Reach for lifetimes only
when the borrowed data provably outlives the struct.
&str over &String in parameters — accept the more general type.
Cow<'_, str> when a function sometimes allocates and sometimes doesn't.
Return owned types from public APIs unless there's a clear performance reason for
references.
Type System
Rust's type system is expressive enough to encode most invariants at compile time. Use it.
Make illegal states unrepresentable. Encode invariants in types so invalid
configurations don't compile. A Connection<Authenticated> that can only be constructed
after login is stronger than a runtime is_authenticated check.
Enums over strings or sentinel values.Status::Active is safer and more
self-documenting than "active". Strings are for human-facing text, not program state.
Newtypes for domain concepts.struct UserId(u64) prevents accidentally passing a
PostId where a UserId is expected — a class of bug that's invisible with raw integers.
Exhaustive pattern matching. Avoid catch-all _ => arms when matching enums. Listing
each variant explicitly means the compiler flags new additions as errors, forcing you to
handle them.
Avoid boolean parameters.render(true) at the call site is opaque. Use a two-variant
enum: render(Visible::Yes) reads immediately.
Error Handling
Result<T, E> for recoverable errors. Never panic for expected failure modes.
? for propagation. Don't manually match on Result just to re-wrap — the ?
operator handles conversion via From.
thiserror for library/crate-internal error types where callers need to match variants.
anyhow::Result for application-level code where callers just need the error message.
Avoid unwrap() and expect() in production paths. Reserve them for cases where the
invariant is provably guaranteed, and add a comment explaining why.
Option for absence, Result for failure. If an error message would be useful,
use Result.
Pattern Matching & Control Flow
Exhaustive matching over catch-all. List all enum variants explicitly. The compiler
becomes your changelog reviewer.
if let for single-variant checks. A full match for one arm is noise.
matches!() for boolean pattern checks:matches!(value, Pattern::A | Pattern::B)
Early returns for guard clauses. Reduce nesting by returning early on error/edge
conditions.