best-practices-rust
Repo-specific Rust best practices for agentic coding: thiserror + anyhow error handling, tokio async runtime, tracing for observability, clap for CLI, axum for HTTP, serde patterns, cargo workspace conventions, ownership and lifetimes, and idiomatic Rust 2024 edition patterns.
- License 许可
- MIT
- 最近更新
- 2026-06-13
- Stars
- ★ 0
- Forks
- 2
01 前言 Front Matter
- name
- best-practices-rust
- license
- MIT
- composes
- []
- metadata
- { "edition": "2024", "defaults": { "cli": "clap (derive)", "tui": "ratatui + crossterm", "style": { "naming": { "types": "PascalCase", "constants": "SCREAMING_SNAKE_CASE", "functions": "snake_case" }, "indentation": "4 spaces", "max_line_length": 100 }, "logging": "tracing + tracing-subscriber", "testing": { "runner": "cargo test", "framework": "built-in (#[test])" }, "progress": "indicatif", "packaging": "cargo", "http_client": "reqwest", "http_server": "axum", "parallelism": "rayon", "async_runtime": "tokio", "serialization": "serde + serde_json", "error_handling": { "libraries": "thiserror", "applications": "anyhow" }, "data_processing": "polars" }, "language": "rust", "rust_versions": [ "1.85+" ] }
- provides
- [ "best-practices-rust" ]
- taxonomy
- [ "validation", "precision", "resilience" ]
- triggers
- [ "best practices rust", "rust conventions", "rust code review", "cargo workspace", "thiserror anyhow", "tokio async", "rust error handling", "axum server", "clap cli", "rust performance", "rust ownership lifetimes", "rust edition 2024" ]
- description
- Repo-specific Rust best practices for agentic coding: thiserror + anyhow error handling, tokio async runtime, tracing for observability, clap for CLI, axum for HTTP, serde patterns, cargo workspace conventions, ownership and lifetimes, and idiomatic Rust 2024 edition patterns.
03 SKILL.md 正文
Rust Best Practices (Project Skill)
This skill is a curated set of atomic rules for writing and refactoring Rust in this repo. Assume the reader is a Python expert but a Rust novice. Include additional code comments around Rust-specific nuances that a Python developer may not recognize.
Advanced Patterns Reference
See references/advanced-patterns.md for deep-dive content:
sealed traits, extension traits, HRTBs, 'static misconceptions, Pin/Unpin, smart pointer
decision tree, interior mutability, serde advanced patterns, tracing layers, CI pipeline tools,
property-based testing, snapshot testing, and Rust 2024 edition features.
Project Defaults (apply unless explicitly overridden)
- Error Handling:
thiserrorfor library crates,anyhowfor application crates - Async Runtime:
tokio(multi-threaded by default) - HTTP Server:
axumwithtowermiddleware - HTTP Client:
reqwest - CLI:
clapwith derive macros - Serialization:
serde+serde_json - Logging:
tracing+tracing-subscriber(NEVERprintln!for diagnostics) - TUI:
ratatui+crossterm - Data Processing:
polars(NEVER other dataframe libraries) - CPU Parallelism:
rayon - Progress Bars:
indicatifwith contextually sensitive messages - Packaging:
cargofor builds, deps, and workspace management
When to Apply
Use this skill whenever you:
- create or refactor Rust crates, binaries, libraries, or workspaces
- add error handling, async code, or network calls
- change
Cargo.tomldependencies or workspace structure - add tests or fix bugs
- build WASM or PyO3/maturin bridges
Categories (priority order)
- Correctness & Ownership (CRITICAL/HIGH):
correctness- - Security (CRITICAL/HIGH):
security- - Error Handling (CRITICAL):
errors- - Type System (HIGH):
types- - Conventions (HIGH):
conventions- - Async & Concurrency (HIGH/MEDIUM):
async- - Testing (HIGH/MEDIUM):
testing- - Performance & Memory (MEDIUM):
perf- - Cargo & Workspaces (MEDIUM):
cargo- - Documentation (MEDIUM):
docs- - Style & Maintainability (MEDIUM/LOW):
style-
Quick Reference (house rules)
correctness-ownership— understand move vs borrow; prefer borrowingcorrectness-lifetimes— let the compiler infer; annotate only when requiredcorrectness-clone-explicit— call.clone()explicitly on non-Copytypescorrectness-let-else— uselet-elsefor early returns on pattern failureerrors-thiserror-libraries— usethiserrorin library crateserrors-anyhow-applications— useanyhowin application/binary crateserrors-no-unwrap— NEVER.unwrap()in production codeerrors-context— add.context()to every?at module boundarieserrors-boundary-pattern— three zones: library/app/API boundaryconventions-tracing— usetracingfor all observabilityconventions-clap-derive— useclapderive macros for CLIconventions-serde— useserdefor all serializationconventions-reqwest— usereqwestwith shared client for HTTPconventions-no-emoji— NEVER use emoji or unicode emoji-like charsconventions-cargo-deps-complete— everyuseneeds aCargo.tomlentryconventions-function-design— single responsibility, max 5 paramsconventions-no-debug-output— NEVER commitprintln!/dbg!types-newtype— use newtypes to distinguish semantically different valuestypes-typestate— use the Type State Pattern for compile-time state machinestypes-sealed-trait— seal public traits in libraries to allow evolutiontypes-option-over-sentinel— preferOption<T>over sentinel valuesasync-structured— use structured concurrency withJoinSetasync-spawn-blocking— offload CPU-bound work tospawn_blockingasync-send-sync— understandSend/Syncbounds for threaded runtimesasync-cancellation— useCancellationTokenfor graceful shutdownasync-mutex-vs-rwlock—RwLockfor read-heavy,Mutexfor simple exclusiveperf-borrow-first— prefer&Tover ownedTin function paramsperf-cow— useCow<'_, str>when ownership is conditionally neededperf-vec-capacity— useVec::with_capacity()when size is knownperf-iterators— prefer iterator combinators over manual loopstesting-arrange-act-assert— follow AAA pattern in all teststesting-proptest— property-based tests for roundtrip/invariant checkstesting-doc-tests— doc examples are compiled and run bycargo testcargo-workspace— share deps via[workspace.dependencies]cargo-features— additive feature flags, minimal defaultscargo-ci-tools—cargo-deny,cargo-audit,cargo-machetein CIstyle-rustfmt— all code formatted withrustfmtstyle-clippy-clean— zero clippy warnings (-D warnings)docs-public-items— doc comments on all public items
1. Correctness & Ownership (CRITICAL)
Rust's ownership system is the biggest difference from Python. In Python, everything is reference-counted and garbage-collected. In Rust, each value has exactly one owner, and the compiler enforces this at compile time — no GC overhead, no use-after-free.
Rule: correctness-ownership
Understand the three modes of passing data:
// MOVE: caller gives up ownership (like Python's only mode, but enforced)
fn consume(data: String) { /* data is owned here, freed when function ends */ }
// BORROW: caller lends read-only access (like a const reference)
fn inspect(data: &str) { /* can read data, can't modify or store it */ }
// MUTABLE BORROW: caller lends exclusive write access
fn modify(data: &mut String) { /* can modify, but no other refs can exist */ }
Default to borrowing. Only take ownership when you need to store or move the value.
Think of &T as Python's normal function args, and owned T as "I'm taking this from you."
Rule: correctness-lifetimes
Let the compiler infer lifetimes. Only annotate when the compiler asks:
// GOOD: compiler infers lifetimes automatically (elision rules)
fn first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
// ONLY annotate when the compiler needs help (multiple references in, one out)
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 'a means: the returned reference lives as long as BOTH inputs.
// Python equivalent: this is like guaranteeing the return value won't
// outlive either of the inputs — the compiler checks this for you.
Rule: correctness-clone-explicit
MUST call .clone() explicitly on non-Copy types. Never hide clones in closures:
// BAD: hidden clone inside closure — easy to miss the allocation
let names: Vec<String> = items.iter().map(|i| i.name).collect(); // won't compile
// GOOD: explicit clone makes the cost visible
let names: Vec<String> = items.iter().map(|i| i.name.clone()).collect();
// BETTER: borrow if you don't need ownership
let names: Vec<&str> = items.iter().map(|i| i.name.as_str()).collect();
Rule: correctness-let-else
Use let-else for early returns on pattern match failure (Rust 1.65+):
// BAD: nested match
fn process(input: Option<&str>) -> Result<()> {
match input {
Some(val) => {
// deeply nested logic...
}
None => return Err(anyhow!("missing input")),
}
}
// GOOD: flat control flow with let-else
fn process(input: Option<&str>) -> Result<()> {
let Some(val) = input else {
return Err(anyhow!("missing input"));
};
// val is bound here, no nesting
Ok(())
}
2. Security (CRITICAL)
Rule: security-no-secrets-in-code
NEVER store secrets, API keys, or passwords in code. Use .env files:
use dotenvy::dotenv;
fn main() {
dotenv().ok();
let api_key = std::env::var("API_KEY")
.expect("API_KEY must be set");
}
Ensure .env is in .gitignore.
Rule: security-no-log-secrets
NEVER log sensitive information. Use the secrecy crate for sensitive data types:
use secrecy::{Secret, ExposeSecret};
struct Config {
api_key: Secret<String>, // Debug/Display print "[REDACTED]"
}
// Only expose when actually needed for the API call
fn make_request(config: &Config) {
let key = config.api_key.expose_secret();
// key is &str — use it, then it's dropped
}
Rule: security-no-unsafe
NEVER use unsafe unless absolutely necessary. When required, document safety invariants
with a // SAFETY: comment explaining why the invariants hold:
// SAFETY: `ptr` is guaranteed non-null and properly aligned because
// it was obtained from `Box::into_raw` on the line above, and no
// other code has accessed or freed it since.
unsafe { *ptr = value; }
Rule: security-input-validation
Validate all external input at system boundaries. Use newtypes with validation:
struct Port(u16);
impl Port {
fn new(value: u16) -> Result<Self, anyhow::Error> {
anyhow::ensure!(value > 0 && value < 65536, "invalid port: {value}");
Ok(Self(value))
}
}
3. Error Handling (CRITICAL)
Rule: errors-thiserror-libraries
Library crates MUST define structured error types with thiserror:
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ParseError {
#[error("invalid header at byte {offset}: {reason}")]
InvalidHeader { offset: usize, reason: String },
#[error("unsupported version {0}")]
UnsupportedVersion(u32),
// #[from] auto-implements From<std::io::Error> for ParseError
#[error(transparent)]
Io(#[from] std::io::Error),
}
Rule: errors-anyhow-applications
Application/binary crates use anyhow for ad-hoc error context:
use anyhow::{Context, Result};
fn load_config(path: &Path) -> Result<Config> {
let contents = std::fs::read_to_string(path)
.context("failed to read config file")?; // Always add context
let config: Config = toml::from_str(&contents)
.with_context(|| format!("failed to parse {}", path.display()))?;
Ok(config)
}
Rule: errors-no-unwrap
- NEVER use
.unwrap()in production code paths - Use
.expect("invariant: reason")ONLY for true invariant violations with a descriptive message - In library code, NEVER use
.unwrap()or.expect()— always returnResult
Rule: errors-context
Every ? propagation at a module boundary MUST have .context() or .with_context():
// BAD: raw ? loses context — error says "No such file" with no clue WHICH file
let data = std::fs::read(path)?;
// GOOD: context explains what we were trying to do
let data = std::fs::read(path)
.with_context(|| format!("reading input file {}", path.display()))?;
Rule: errors-boundary-pattern
Error handling has three zones:
- Library internals —
thiserrorenums, noanyhow - Application logic —
anyhow::Result,.context()everywhere - API boundaries (axum handlers, CLI) — convert to user-facing messages, log internal details
// axum handler: boundary between internal errors and HTTP responses
async fn get_item(Path(id): Path<u64>) -> Result<Json<Item>, AppError> {
let item = db::find_item(id)
.await
.context("database lookup failed")?; // anyhow context
Ok(Json(item))
}
// AppError converts anyhow::Error to HTTP status + log
struct AppError(anyhow::Error);
impl IntoResponse for AppError {
fn into_response(self) -> Response {
tracing::error!(error = %self.0, "request failed");
(StatusCode::INTERNAL_SERVER_ERROR, "internal error").into_response()
}
}
See RULES_DETAILED.md for detailed rules on type system, conventions, async/concurrency, axum, testing, performance, cargo workspaces, documentation, and style.
04 同仓库更多 Skills
best-practices-streamdeck
Project-specific best practices for the Stream Deck control suite: icon format constraints, socket vs config boundaries, widget button lifecycle, page creation via build_page, ArangoDB fallback patterns, and NVIS palette compliance.
best-practices-scillm
Error recovery and anti-patterns for scillm LLM proxy. Load this when scillm calls fail, timeout, or return unexpected results. Covers batch sizing, header requirements, model selection, and debugging workflow.
react-native-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
composition-patterns
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.
best-practices-react
React, Next.js, React Native, and web design best practices from Vercel Engineering. Use when writing, reviewing, or refactoring React/Next.js/React Native components, optimizing performance, auditing UI accessibility, or designing component APIs. Triggers: "review react", "optimize component", "check accessibility", "react best practices", "component architecture", "react native performance".