Rust Standards
Standards for Rust code.
Edition
- Use the latest stable edition (2021+)
- Set
editionexplicitly inCargo.toml
Toolchain
- Follow the
lintskill for formatting and linting — lintro runsrustfmt,clippy,cargo_audit, andcargo_denyas configured - Treat clippy warnings as errors in CI (
-D warnings) - Customize formatting via
rustfmt.tomlwhere needed
Error Handling
Use
thiserrorfor library error types — derive structured, typed errorsUse
anyhowfor application-level error propagationNo
.unwrap()in library code — use?or returnResult.expect()only with descriptive messages explaining the invariant:// Good let config = load_config().expect("config.toml must exist at startup"); // Bad let config = load_config().unwrap();Implement
std::fmt::Displayfor all custom error typesNon-panicking
.unwrap_or_default()/.unwrap_or()over trivial-arm matches:// Don't let count = match maybe_count { Some(n) => n, None => 0, }; // Do let count = maybe_count.unwrap_or(0);
Type Patterns
- Prefer newtypes for domain concepts —
struct UserId(u64)over bareu64 - Use
impl Traitin argument position for flexibility; explicit generics in return position for clarity - Derive
Debugon all public types - Derive
Clone,PartialEq,Eq,Hashwhere semantically appropriate - Prefer
&stroverStringin function arguments; returnStringwhen ownership transfers
Unsafe
unsafeblocks MUST include a// SAFETY:comment justifying soundness:// SAFETY: pointer is guaranteed non-null by the allocator contract, // and the lifetime is bounded by the enclosing scope. unsafe { ptr.as_ref() }Minimize unsafe surface area — encapsulate in safe abstractions
Prefer safe alternatives (e.g.,
std::sync::Mutexover raw atomics) unless performance demands otherwise
Documentation
///doc comments on all public items (functions, types, traits, modules)Include code examples in doc comments for non-trivial APIs:
/// Parse a duration string like "5s", "100ms", or "2m". /// /// # Examples /// /// ``` /// use mycrate::parse_duration; /// /// let d = parse_duration("5s").unwrap(); /// assert_eq!(d, std::time::Duration::from_secs(5)); /// ``` pub fn parse_duration(s: &str) -> Result<Duration> { ... }Use
#![deny(missing_docs)]for library cratesModule-level
//!doc comments for crate and module overviews
Testing
- Unit tests in
#[cfg(test)] mod testswithin the same file - Integration tests in
tests/directory - Use
#[should_panic(expected = "...")]for expected panics - Consider
proptestorquickcheckfor property-based testing where valuable - Use
assert_eq!andassert_ne!over bareassert!for better error messages
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_duration_seconds() {
let d = parse_duration("5s").unwrap();
assert_eq!(d, Duration::from_secs(5));
}
#[test]
#[should_panic(expected = "invalid format")]
fn parse_duration_rejects_garbage() {
parse_duration("not_a_duration").unwrap();
}
}
Dependencies
- Keep the dependency tree minimal — every dependency is an audit and supply chain surface
- Run vulnerability scanning via lintro (
uv run lintro chkincludescargo_auditandcargo_deny) - Pin versions in workspace
Cargo.tomlfor multi-crate workspaces - Prefer well-maintained crates with active maintainers and good documentation
Patterns
Prefer
implblocks over free functions for associated behaviorUse the builder pattern for types with many optional fields
Prefer iterators and combinators over manual loops where readability permits
let-elseover nestedif letpyramids:// Don't if let Some(user) = lookup(id) { if let Some(email) = user.email { send(email); } } // Do let Some(user) = lookup(id) else { return }; let Some(email) = user.email else { return }; send(email);.find()/.position()/.any()over manual index loops:// Don't let mut idx = None; for (i, item) in items.iter().enumerate() { if item.id == target { idx = Some(i); break; } } // Do let idx = items.iter().position(|item| item.id == target);matches!()for pattern booleans:// Don't let is_ready = match state { State::Ready => true, _ => false, }; // Do let is_ready = matches!(state, State::Ready);Use
#[must_use]on functions whose return value should not be ignoredPrefer
From/Intoimplementations over ad-hoc conversion methods
Linting
Follow the lint skill for linting and formatting workflow.