agentleFS
motola10motola10/claude-code-base/.claude/skills/rust-standards/SKILL.md

rust-standards

Skill1 starsChanged 5 months agoMITView on GitHub

Best-practice coding standards for Rust code (manifest: Cargo.toml). Self-contained — covers toolchain/edition, ownership & borrowing, type design, error handling (Result/?, thiserror/anyhow), unsafe, async/concurrency (tokio), security, logging (tracing), clippy/rustfmt, testing, plus a lean axum HTTP service section. Used by the review agents on Rust projects. Do NOT use for other languages.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

21 rules. About 0 are common, 3 are distinctive, and 18 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 18

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Target: all Rust source — libraries (crates), CLIs, and async/HTTP services.

    line 50
  2. Distinctive:

    Self-contained: No framework overlay; the async/service section (§11) is part of this document.

    line 51
  3. Very specific:

    RUST-BASE-2 (MUST): Use Cargo; commit Cargo.lock for binaries/services (libraries may omit it).

    line 62
  4. Very specific:

    RUST-BASE-4 (SHOULD): Keep the happy path shallow — return early on error with ?/guard clauses rather than deep nesting.

    line 66
  5. Distinctive:

    RUST-OWN-3 (SHOULD): Keep lifetimes implicit where elision allows.

    line 78
  6. Very specific:

    MUST NOT encode absence/▸failure as sentinel values (-1, empty string, 0).

    line 92
  7. Very specific:

    RUST-TYPE-4 (SHOULD): Prefer iterators and combinators (map/filter/collect) over manual index loops.

    line 95
  8. Very specific:

    RUST-TYPE-5 (SHOULD): Keep the public API minimal — items are private by default.

    line 97
  9. Very specific:

    MUST NOT ignore a Result (#[must_use] — enforce unused_must_use).

    line 105
  10. Very specific:

    RUST-ERR-2 (MUST NOT): Use .unwrap() / .expect() / panic!

    line 106
  11. Very specific:

    RUST-ERR-4 (SHOULD): Add context as errors cross layers (thiserror variants or anyhow::Context) — don't discard the source.

    line 112
  12. Very specific:

    RUST-SAFE-1 (SHOULD): Add #![forbid(unsafe_code)] (or #![deny(unsafe_code)]) to crates that don't need unsafe.

    line 137
  13. Very specific:

    RUST-SEC-4 (SHOULD): Use vetted crypto crates (argon2/bcrypt for password hashing) and a CSPRNG (rand's OsRng / getrandom) for tokens and …

    line 168
  14. Very specific:

    RUST-LOG-1 (MUST): Use the tracing crate for structured, async-aware logging and spans.

    line 175
  15. Very specific:

    MUST NOT use println!/eprintln!

    line 176
  16. Very specific:

    RUST-LOG-3 (SHOULD): Use spans to carry request/trace context for correlation.

    line 179
  17. Very specific:

    RUST-TEST-1 (MUST): Test with the built-in framework (cargo test): unit tests in a #[cfg(test)] mod tests, integration tests under tests/.

    line 196
  18. Very specific:

    RUST-TEST-3 (SHOULD): Follow the test pyramid — many fast unit tests, fewer integration, few end-to-end.

    line 201
  19. Very specific:

    RUST-API-2 (MUST): Use typed extractors (Json<T>, Path<T>, Query<T>, State<T>) and share state via State (an Arc-wrapped app state) — MUST …

    line 217
  20. Very specific:

    RUST-API-4 (SHOULD): Default auth scheme is stateless JWT bearer (token in Authorization: Bearer), verified in a tower middleware/extractor.

    line 224
  21. Very specific:

    RUST-DOC-1 (SHOULD): Write /// doc comments on public items (what it does, params, errors, panics), with runnable doctest examples where …

    line 241

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Rust Coding Standard (v1.0)

Status: Locked v1.0. Same RFC-2119 constraint language, RUST-AREA-N rule-ID format, and severity mapping as the rest of the suite. Like go.md, Rust is a self-contained standard: it covers the language and a lean async/HTTP-service section (§11), rather than deferring to a separate framework overlay. Where it sets API/auth/logging defaults it stays consistent with nodejs.md / fastapi.md / go.md / java.md so a polyglot codebase behaves the same way.

On adoption this becomes .claude/skills/rust-standards/SKILL.md + references/*.md.

Toolchain verified current as of 2026-06-13 (via Context7 + crates.io): Rust 1.96.x (edition 2024), clippy/rustfmt (rustup components), tokio 1.52.x, axum 0.8.x, serde 1.x, thiserror 2.x, anyhow 1.x, sqlx 0.9.x, tracing 0.1.x. Rules are written version-agnostically; these are reference points, not pins.


How to read this document

Constraint language (RFC 2119)

  • MUST / MUST NOT — hard requirement; a violation is a blocking issue.
  • SHOULD / SHOULD NOT — strong recommendation; deviate only with a written // Justification:.
  • MAY — optional.

Rule IDs

Stable RUST-AREA-NUMBER (e.g. RUST-ERR-3). The reviewer MUST cite the rule ID. Never renumber.

Severity mapping

This document says…Reviewer severityGate
MUST/MUST NOT affecting safety (unsafe/panics) / securityCriticalBlocks merge
MUST/MUST NOT elsewhereCritical / Warning (by impact)Blocks if it risks correctness
SHOULD without justificationWarningShould fix
MAY / stylisticSuggestionNice to have

Scope & Assumptions

  1. Target: all Rust source — libraries (crates), CLIs, and async/HTTP services.
  2. Self-contained. No framework overlay; the async/service section (§11) is part of this document.
  3. Safe Rust by default. unsafe is the rare, justified exception (§5), not a tool of convenience.
  4. Custom overrides: the project's custom-standards skill overrides this document on conflict.

1. Toolchain, Edition & Cargo

  • RUST-BASE-1 (MUST): Build on stable Rust with the current edition (2024) declared in Cargo.toml. Declare a MSRV (rust-version) and keep CI on it. Nightly only with a written justification for a specific feature.
  • RUST-BASE-2 (MUST): Use Cargo; commit Cargo.lock for binaries/services (libraries may omit it). Keep dependencies minimal and audited; pin via the lockfile.
  • RUST-BASE-3 (MUST): Code MUST be rustfmt-clean (cargo fmt --check in CI). Formatting is not reviewed by hand.
  • RUST-BASE-4 (SHOULD): Keep the happy path shallow — return early on error with ?/guard clauses rather than deep nesting.

2. Ownership & Borrowing

  • RUST-OWN-1 (SHOULD): Borrow rather than clone. Take &T/&mut T (or &str/&[T]) in function signatures; don't .clone() to dodge the borrow checker. An intentional clone carries a // Justification: or an obvious reason.
  • RUST-OWN-2 (SHOULD): Accept the most general borrowed type (&str over &String, &[T] over &Vec<T>, impl AsRef<Path> over &PathBuf) so callers aren't forced to allocate.
  • RUST-OWN-3 (SHOULD): Keep lifetimes implicit where elision allows; introduce explicit lifetimes only when the compiler requires them. Don't over-annotate.
  • RUST-OWN-4 (SHOULD NOT): Reach for Rc/RefCell (or Arc<Mutex<…>>) to escape ownership design when restructuring data flow would remove the need. Shared interior mutability is a deliberate choice, not a default.

3. Types & API Design

  • RUST-TYPE-1 (SHOULD): Make illegal states unrepresentable — model variants with enums and match exhaustively; use newtypes (struct UserId(u64)) over bare primitives for identifiers and units (avoid primitive obsession).
  • RUST-TYPE-2 (SHOULD): Return Option<T> for "absent," Result<T, E> for "fallible." MUST NOT encode absence/▸failure as sentinel values (-1, empty string, 0).
  • RUST-TYPE-3 (SHOULD): Derive standard traits where they make sense (Debug almost always; Clone, PartialEq, Eq, Hash as needed). Public types SHOULD implement Debug.
  • RUST-TYPE-4 (SHOULD): Prefer iterators and combinators (map/filter/collect) over manual index loops; they're clearer and bounds-check-free.
  • RUST-TYPE-5 (SHOULD): Keep the public API minimal — items are private by default; expose pub only what callers need. Use #[non_exhaustive] on public enums/structs likely to grow.

4. Error Handling

  • RUST-ERR-1 (MUST): Model fallible operations with Result<T, E> and propagate with ?. MUST NOT ignore a Result (#[must_use] — enforce unused_must_use).
  • RUST-ERR-2 (MUST NOT): Use .unwrap() / .expect() / panic! on recoverable errors in library or service code paths. They're acceptable only in tests, examples, main() startup wiring, or a genuinely-unreachable invariant documented with expect("reason").
  • RUST-ERR-3 (SHOULD): Libraries define typed errors with thiserror (derive Error, implement std::error::Error, preserve #[source]). Binaries/services MAY use anyhow for opaque, context-rich errors (.context("...")).
  • RUST-ERR-4 (SHOULD): Add context as errors cross layers (thiserror variants or anyhow::Context) — don't discard the source. MUST NOT stringify-and-lose the underlying error.
  • RUST-ERR-5 (SHOULD NOT): Convert errors to panics at layer boundaries to "simplify" signatures. Return the Result; let the top decide.
// ✅ RUST-ERR-1/3/4: typed error, `?`, preserved source
#[derive(Debug, thiserror::Error)]
pub enum LoadError {
    #[error("user {0} not found")]
    NotFound(String),
    #[error("database error")]
    Db(#[from] sqlx::Error), // source preserved
}

pub async fn load_user(pool: &sqlx::PgPool, id: &str) -> Result<User, LoadError> {
    let user = fetch(pool, id).await?;                 // `?` propagates Db
    user.ok_or_else(|| LoadError::NotFound(id.into()))
}

5. Unsafe Code

  • RUST-SAFE-1 (SHOULD): Add #![forbid(unsafe_code)] (or #![deny(unsafe_code)]) to crates that don't need unsafe. Most application/service code should never write unsafe.
  • RUST-SAFE-2 (MUST): Where unsafe is unavoidable, keep each block minimal, wrap it in a safe abstraction, and document the invariants with a // SAFETY: comment explaining why it's sound. Unreviewed/undocumented unsafe is a blocking issue.

6. Concurrency & Async

  • RUST-CONC-1 (SHOULD): Let the type system enforce thread-safety — share state via Arc, guard mutation with Mutex/RwLock (or message-passing channels). The compiler's Send/Sync checks are the safety net; don't bypass them with unsafe.
  • RUST-CONC-2 (MUST): Pick one async runtime — tokio is the default — and use its primitives consistently. MUST NOT block the async executor: never call blocking I/O or std::thread::sleep in an async fn; use async equivalents or tokio::task::spawn_blocking.
  • RUST-CONC-3 (MUST NOT): Hold a std::sync::Mutex guard across an .await point — use tokio::sync::Mutex when a lock must span awaits, or restructure to drop the guard first.
  • RUST-CONC-4 (SHOULD): Propagate cancellation via the runtime; ensure spawned tasks have a clear join/shutdown path. Don't spawn and forget — handle the JoinHandle or tie it to shutdown.

7. Security

  • RUST-SEC-1 (MUST NOT): Build SQL by string concatenation. Use parameterized/bound queries (e.g. sqlx bind parameters — RUST-API-6).
  • RUST-SEC-2 (MUST NOT): Hard-code secrets. Load credentials/keys from environment or a secret manager; zero sensitive buffers (zeroize) where it matters; never commit them.
  • RUST-SEC-3 (MUST): Audit dependencies with cargo audit (RustSec advisory DB) in CI; keep crates patched. Vet new dependencies — every crate is supply-chain surface.
  • RUST-SEC-4 (SHOULD): Use vetted crypto crates (argon2/bcrypt for password hashing) and a CSPRNG (rand's OsRng / getrandom) for tokens and salts — never a non-cryptographic RNG.

8. Logging & Observability

  • RUST-LOG-1 (MUST): Use the tracing crate for structured, async-aware logging and spans. MUST NOT use println!/eprintln! for application logging in production code.
  • RUST-LOG-2 (MUST): Emit structured fields with a level, and MUST NOT log secrets, tokens, or PII. (Consistent with NODE-LOG-2.)
  • RUST-LOG-3 (SHOULD): Use spans to carry request/trace context for correlation; expose health/readiness for services. (Consistent with NODE-LOG-3.)

9. Tooling & Linting

  • RUST-LINT-1 (MUST): cargo clippy MUST pass in CI with warnings denied (cargo clippy -- -D warnings). Don't blanket-#[allow(...)]; suppress a single lint inline with a justified #[allow(clippy::...)] // reason.
  • RUST-LINT-2 (MUST): The build MUST be warning-clean (#![deny(warnings)] in CI or RUSTFLAGS=-Dwarnings). Treat compiler warnings as errors.

10. Testing

  • RUST-TEST-1 (MUST): Test with the built-in framework (cargo test): unit tests in a #[cfg(test)] mod tests, integration tests under tests/. New or changed behavior ships with tests; changed logic with no test is insufficient coverage.
  • RUST-TEST-2 (MUST): Tests are deterministic and isolated — no real external network in unit tests, no order dependency, no shared mutable global state.
  • RUST-TEST-3 (SHOULD): Follow the test pyramid — many fast unit tests, fewer integration, few end-to-end. Target ≥ 80% coverage on changed code (not whole-repo) via cargo llvm-cov (or tarpaulin); coverage is a floor, not the goal. (Consistent with NODE-TEST-3 / PY-TEST-4.)
  • RUST-TEST-4 (SHOULD): Name tests by behavior; use doctests (/// examples that compile and run) for public-API usage. unwrap/expect are fine inside tests (RUST-ERR-2 exempts tests).

11. Async HTTP Services & APIs

Cross-platform consistency: these mirror nodejs.md / fastapi.md / go.md / java.md so auth, versioning, and the data layer behave the same across the stack.

  • RUST-API-1 (SHOULD): Build async HTTP services on axum (over tokio + tower/hyper) — the idiomatic modern default. Other tower-based frameworks (e.g. actix-web) are sanctioned where a clear need justifies them.
  • RUST-API-2 (MUST): Use typed extractors (Json<T>, Path<T>, Query<T>, State<T>) and share state via State (an Arc-wrapped app state) — MUST NOT use global mutable statics.
  • RUST-API-3 (MUST): Validate and treat all request input as untrusted — deserialize into typed structs (serde), validate fields, and reject malformed input with the right 4xx. validator (derive-based field constraints) is the recommended default validation crate (recommended not mandated) — the Rust counterpart to Zod (TS-TYPE-2) / Bean Validation (JAVA-SPRING-4) / Pydantic. Set request timeouts/body limits (via tower layers).
  • RUST-API-4 (SHOULD): Default auth scheme is stateless JWT bearer (token in Authorization: Bearer), verified in a tower middleware/extractor; check authorization (roles/scopes), not just authentication. Sanctioned alternatives by context: httpOnly session cookies (browser-first) or an external IdP / OAuth2 provider. (Consistent with NODE-NEST-14 / FASTAPI-SEC-1 / GO-API-4 / JAVA-SPRING-8.)
  • RUST-API-5 (SHOULD): Version public APIs with URI versioning /v1 in the route path. (Consistent with NODE-NEST-15 / FASTAPI-DOC-2 / GO-API-5.)
  • RUST-API-6 (SHOULD): Access the database through a repository abstraction with sqlx (compile-time-checked, async, bound parameters — RUST-SEC-1) or a vetted alternative (SeaORM/Diesel); run schema changes through versioned migrations (sqlx migrate). Keep raw queries out of handlers. (Consistent with the repo-layer + migrations default in NODE-NEST-16 / FASTAPI-STRUCT-5 / GO-API-6 / JAVA-SPRING-7.)

12. Documentation

  • RUST-DOC-1 (SHOULD): Write /// doc comments on public items (what it does, params, errors, panics), with runnable doctest examples where useful. A crate has a //! crate-level doc.

Resolved Decisions (v1.0)

#QuestionDecisionRule(s)
1Web frameworkaxum default (on tokio+tower+hyper); actix-web sanctioned. Matches GO-API-1 / JAVA-SPRING-11 named-default+sanctioned-alternativeRUST-API-1
2App error cratethiserror for libraries / anyhow for binaries — the established Rust library-vs-application convention; named per-context, not collapsedRUST-ERR-3
3DB / ORMsqlx recommended default (compile-time-checked, bound params); SeaORM/Diesel sanctioned. Recommended not mandated, but bound params + repo boundary + migrations required. Matches GO-API-6 / FASTAPI-STRUCT-5RUST-API-6, RUST-SEC-1
4Validationvalidator recommended default boundary-validation crate (recommended not mandated) — the Rust counterpart to Zod / Bean Validation / PydanticRUST-API-3

Toolchain reference (verified 2026-06-13): Rust 1.96.x (edition 2024), tokio 1.52.x, axum 0.8.x, serde 1.x, thiserror 2.x, anyhow 1.x, sqlx 0.9.x, validator 0.20.x, tracing 0.1.x.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.