Claude
Skills
Sign in
Back

rust

Included with Lifetime
$97 forever

Rust language conventions, idioms, error handling, concurrency (CPU-bound parallelism with threads/rayon and I/O-bound async with Tokio), the cargo/clippy/rustfmt toolchain, and rustdoc. Invoke whenever task involves any interaction with Rust code — writing, reviewing, refactoring, debugging, or understanding .rs files and Cargo projects.

Backend & APIs

What this skill does


# Rust

Lean on the type system and the borrow checker. They are the design surface, not an obstacle. When the compiler rejects
code, the ownership model is wrong — restructure who owns and who borrows rather than reaching for `.clone()`, `Rc`, or
`unsafe` to silence it. Make illegal states unrepresentable: encode invariants in types so the wrong code fails to
compile instead of failing at runtime. A fight with the borrow checker is a design review the compiler is giving you for
free.

## Route to Reference

Read the reference before doing focused work in its area; SKILL.md alone is sufficient for routine code.

- **Idioms and ownership patterns** — `${CLAUDE_SKILL_DIR}/references/idioms.md` borrowing/ownership restructuring,
  `impl Trait` vs generics vs `dyn`, iterator adapters, `match`/`if let`/`let else`, newtype and builder patterns,
  `From`/`Into`/`TryFrom`, the `as_`/`to_`/`into_` cost cheat-table
- **Error handling** — `${CLAUDE_SKILL_DIR}/references/errors.md` `Result`/`Option`/`?`, `thiserror` enum templates,
  `anyhow` `.context()`, the panic/`unwrap`/`expect` policy, error-type design (`Display`/`Error`/`Send`/`Sync`)
- **Anti-patterns** — `${CLAUDE_SKILL_DIR}/references/anti-patterns.md` AI-produced anti-patterns mapped to the Clippy
  lints that catch them, with the idiomatic fix for each
- **API design** — `${CLAUDE_SKILL_DIR}/references/api-guidelines.md` the Rust API Guidelines `C-*` checklist, RFC 430
  naming, `#[non_exhaustive]`, `#[must_use]`, common-trait derives, sealed traits, builders
- **Parallelism** — `${CLAUDE_SKILL_DIR}/references/parallelism.md` threads, scoped threads, rayon, `Send`/`Sync`,
  channels, shared state (`Arc`/`Mutex`) — the CPU-bound multicore path
- **Async** — `${CLAUDE_SKILL_DIR}/references/async.md` the `Future`/executor model, Tokio, the compiler-invisible
  footgun checklist (blocking in async, holding `!Send` across `.await`), "do you even need async?"
- **Project structure** — `${CLAUDE_SKILL_DIR}/references/structure.md` `mod.rs`-free layout, cargo workspaces, the
  `[lints]`/`[workspace.lints]` table, profiles, features, MSRV
- **Toolchain** — `${CLAUDE_SKILL_DIR}/references/toolchain.md` the fmt/clippy/nextest/deny verification gates,
  rust-analyzer configuration, recommended compiler and Clippy lint sets
- **Edition 2024** — `${CLAUDE_SKILL_DIR}/references/edition-2024.md` Rust 2024 edition specifics and migration via
  `cargo fix --edition`
- **Documentation** — `${CLAUDE_SKILL_DIR}/references/documentation.md` rustdoc conventions, doctests, `missing_docs`,
  intra-doc links, semver-checks

## Naming

- Casing follows RFC 430: `UpperCamelCase` for types/traits/enum variants, `snake_case` for functions/methods/modules,
  `SCREAMING_SNAKE_CASE` for consts/statics.
- Acronyms are one word in `UpperCamelCase` (`Uuid`, `Stdin`, not `UUID`, `StdIn`); lower-cased in `snake_case`.
- Getters omit the `get_` prefix: `fn first(&self) -> &First`, not `fn get_first`. Use `get` only when one obvious value
  is gotten (e.g. `Cell::get`).
- Ad-hoc conversion methods signal cost through their prefix: `as_` = free borrow-to-borrow view; `to_` = expensive
  (allocates or computes); `into_` = consuming, ownership transfer.
- Collection iterators are `iter` (`&T`), `iter_mut` (`&mut T`), `into_iter` (`T`); the iterator type name matches the
  method (`into_iter` -> `IntoIter`).
- Names use consistent verb-object word order (`ParseAddrError`, matching `ParseIntError`). Drop weasel words —
  `Manager`, `Service`, `Factory`, `Helper`, `Util` rarely belong in a type name; name the thing for what it is
  (`Bookings`, not `BookingService`), append a quality only when it does something specific (`BookingDispatcher`).
- A `Builder` is Rust's name for a factory. Accept `impl Fn() -> Foo` rather than a `FooBuilder` parameter.

## Ownership and Borrowing

- Accept the least-owning type that does the job: `&str` over `&String`, `&[T]` over `&Vec<T>`, `&T` over `T` when not
  consuming. Return owned values; borrow in parameters.
- Treat `.clone()` as a signal, not a fix. A clone to satisfy the borrow checker usually means lifetimes or ownership
  are structured wrong — split the borrow, narrow the scope, or restructure who owns the data.
- Reach for `Rc`/`Arc` only for genuine shared ownership (a graph, a cache, a value with no single owner), not to dodge
  a lifetime annotation.
- Prefer adjusting a function to take `&self`/`&T` over cloning at the call site. Borrow at the narrowest scope so a
  mutable borrow ends before the next access begins (non-lexical lifetimes allow this).
- Use lifetime elision wherever it applies; write explicit lifetimes only when the compiler asks or when they document a
  real relationship between inputs and outputs.

## Error Handling

- Functions that can fail return `Result<T, E>`; absence-not-error returns `Option<T>`. Propagate with `?`, do not
  match-and-rewrap.
- Libraries define typed errors with `thiserror` (one enum, `#[error("...")]` per variant, `#[from]` for source
  conversions). Applications use `anyhow::Result` and attach `.context(...)` at each layer.
- Error types implement `std::error::Error`, `Display`, `Debug`, and are `Send + Sync + 'static` so they cross threads
  and compose with `anyhow`/`Box<dyn Error>`.
- `.unwrap()` and `.expect()` are permitted only on a provable invariant (with `.expect("why this cannot fail")`
  documenting the proof), inside `const` contexts, or in tests. They are never error-propagation.
- A detected programming bug panics; it is not an `Error`. A panic means "stop the program" — never use panics to
  communicate recoverable errors upstream, and never assume a panic will be caught. Genuinely fallible operations
  (parsing, I/O, user input) return `Result`.
- Prefer making invalid states uncompilable over validating at runtime: a `NonEmptyVec` or a parsed `Email` newtype
  beats a runtime check on a raw `Vec`/`String`.

## Traits and Generics

- Static dispatch is the default: generics (`fn f<T: Trait>`) or `impl Trait` in argument and return position.
  Monomorphization keeps it allocation-free and inlinable.
- Use `dyn Trait` only for genuine runtime polymorphism — heterogeneous collections (`Vec<Box<dyn Draw>>`), plugin
  boundaries, or breaking deep monomorphization. Always write `dyn` explicitly (`Box<dyn Error>`, `&dyn Handler`);
  bare-trait-object syntax has been an error since edition 2021.
- `impl Trait` in argument position is shorthand for an anonymous generic; in return position it hides a concrete type.
  Switch to a named generic when the caller must name the type or supply it via turbofish.
- Derive the common traits eagerly where they fit: `Debug` on every public type, plus `Clone`, `Copy`, `PartialEq`,
  `Eq`, `Hash`, `PartialOrd`, `Ord`, `Default` as semantics allow. Implement `Display` for types meant to be read by
  humans (errors, string-like wrappers).
- Implement conversions via the standard traits — `From` (which gives `Into` free), `TryFrom`, `AsRef`, `AsMut` — not
  ad-hoc methods, so generic code and `?` interoperate.

## Iterators and Pattern Matching

- Express transformations as iterator adapter chains (`.iter().filter().map().collect()`), not manual index loops. They
  are bounds-check-friendly, lazy, and clearer.
- Reach for `for x in &collection` over `for i in 0..collection.len()`; index only when the index itself is the data.
- `match` arms must be exhaustive — let the compiler enforce that every variant is handled rather than ending with a
  catch-all `_` that silently swallows new variants.
- Use `if let` / `while let` for single-pattern matches, and `let ... else { return / continue / bail }` to bind-or-
  bail in one line instead of nesting the happy path inside a `match`.
- Destructure in the pattern (function params, `let`, match arms) rather than reaching into fields by `.0`/`.field`
  afterward.

## Concurrency: Choose the Right Model First

**Pick the concurrency model from the workload before writi
Files: 12
Size: 224.6 KB
Complexity: 57/100
Category: Backend & APIs

Related in Backend & APIs