This is a Rust project. All code MUST compile with cargo check, pass
cargo clippy --all-targets -- -D warnings, and be formatted with cargo fmt.
- Run
cargo checkafter EVERY change to .rs files - Run
cargo clippy --all-targets -- -D warningsbefore marking any task complete - Run
cargo fmtbefore committing - NEVER deliver code that doesn't compile
- Read compiler errors COMPLETELY — Rust tells you exactly what's wrong and how to fix it
- Fix errors starting from the FIRST one (later errors are often caused by earlier ones)
- ALWAYS verify a crate exists on crates.io BEFORE adding it to the project
- Use
cargo add <crate>to add dependencies — NEVER edit Cargo.toml manually - Preferred crate ecosystem:
- Async runtime:
tokio(with explicit features: rt-multi-thread, macros, full) - HTTP server:
axum - HTTP client:
reqwest - Serialization:
serde+serde_json - Error handling (libraries):
thiserror - Error handling (applications):
anyhow - CLI:
clap(with derive feature) - Logging/tracing:
tracing+tracing-subscriber - Testing: built-in +
tokio::testfor async +proptestfor property-based
- Async runtime:
- Prefer
&T(borrow) overT(move) in function parameters - Prefer
&stroverStringin function parameters - Prefer
impl Traitover generic<T: Trait>when there's only one trait bound - NEVER use
.clone()just to satisfy the borrow checker — restructure code instead - When the borrow checker complains, try solutions in this EXACT order:
- Adjust scope (move code so borrows don't overlap)
- Use references only when needed, drop them quickly
- Clone ONLY if data is small (<100 bytes) or genuinely needed
- Restructure the algorithm (separate read phase from write phase)
- Smart pointers:
Rc<T>(single-thread) orArc<T>(multi-thread) - Interior mutability:
RefCell<T>orMutex<T>as LAST resort
- ALWAYS use
Result<T, E>for operations that can fail - NEVER use
.unwrap()in production code - Use
.expect("descriptive message")ONLY for programming errors / invariants - Use
?operator for error propagation - Use
thiserrorfor library error types,anyhowfor application errors - Error messages: lowercase, no trailing punctuation
- Chain errors with
.context()or.with_context()for debuggability
std::sync::OnceLockinstead oflazy_static!- HashMap Entry API:
.entry(k).or_insert(v)instead ofcontains_key+insert - Collect-then-mutate instead of mutating during iteration
tokio::sync::Mutex(notstd::sync::Mutex) in async codeimpl Displayandimpl Debugfor all custom types
- NEVER invent crate names — verify on crates.io first
- NEVER use
unsafeunless absolutely necessary; document with// SAFETY: reason - NEVER use
#[allow(unused)]to hide problems - NEVER use
Box<dyn Error>as error type in libraries — usethiserror - NEVER ignore clippy warnings — fix them or justify suppression with comment
- NEVER use
lazy_static!— usestd::sync::OnceLock(Rust 1.80+) - NEVER create functions longer than 50 lines — extract helper functions
- NEVER hardcode secrets, keys, or tokens in source code
snake_casefor functions, variables, modulesPascalCasefor types, traits, enums, enum variantsSCREAMING_SNAKE_CASEfor constants and statics- Max 100 characters per line
- One module per file, files under 300 lines
- Document ALL public items with
///doc comments
- Every public function:
///with description - Include
# Examplessection with compilable code - Include
# Errorssection when function returns Result - Include
# Panicssection if function can panic - Include
# Safetysection for any unsafe code - Module-level docs with
//!
- Write tests BEFORE implementation when possible (TDD)
- Unit tests in same file:
#[cfg(test)] mod tests { ... } - Integration tests in
tests/directory - Run
cargo testafter every implementation - Never hardcode expected values that bypass actual logic verification
- Use
#[should_panic]for expected panic tests - Use
proptestfor property-based testing on complex logic
- Use
tokioas the async runtime (notasync-std) - Every async function doing I/O should return
Result - Use
tokio::sync::mpscfor multi-producer channels - Use
tokio::sync::oneshotfor single-response channels - Always
.awaitfutures — a future that isn't awaited does NOTHING - Use
tokio::select!for racing multiple futures - Add timeouts to all network operations
src/lib.rsfor libraries,src/main.rsfor binaries- Cargo workspace for multi-crate projects
- Shared dependencies in
[workspace.dependencies] - Integration tests in
tests/ - Examples in
examples/ - Benchmarks in
benches/
- Validate ALL external input before processing
- Run
cargo auditto check for known vulnerabilities - Never implement custom cryptography — use
ringorrustls - Never log secrets, tokens, passwords, or PII
- Pin dependency versions (commit Cargo.lock for binaries)
- Use
secrecy::Secret<T>for sensitive data
cargo check --workspace— compilescargo clippy --all-targets -- -D warnings— no warningscargo fmt --check— properly formattedcargo test --workspace— all tests pass- No
.unwrap()in non-test code - No invented crate names
- No
unsafewithout// SAFETY:comment