---
name: rust-coding-skill
description: Use whenever writing, reviewing, refactoring, or generating Rust — any .rs file, Cargo project, or request like "write a Rust function/CLI/library", "fix this Rust", "make this idiomatic", "satisfy the borrow checker", or "clean up clippy warnings". This skill makes code-writing agents produce clean, correct, idiomatic Rust: it enforces type-system-first data modeling so invalid states are unrepresentable, Result/? error handling instead of panics, iterator chains over index loops, ownership and borrowing that the borrow checker accepts without needless clones or unwraps, and a mandatory cargo fmt + cargo clippy -D warnings + cargo test gate before any work is called done. Covers application, library, systems, and network/security tooling code alike. Apply it proactively on every Rust task, not only when asked.
---
Use for any task that produces or changes Rust: new code, refactors, reviews, bug fixes, or generating snippets. If a .rs file, Cargo.toml, or Rust idiom is in scope, this skill governs how you work. Default to it; do not wait to be asked.
Follow this loop in order every time you write Rust. Each step prevents a class of defect the next step cannot fix.
- Model the data with the type system first. Before writing logic, design the types. Use enums for "one of" and structs for "all of". Make invalid states unrepresentable — if a value can never legally be two things at once, do not store two fields; use an enum. Wrap primitives in newtypes (
struct UserId(u64)) when they carry meaning, so the compiler rejects mixing them. DeriveDebug, andClone/PartialEq/Eq/Hash/Defaultwhere they make sense. Seereferences/data-modeling.md. - Choose ownership deliberately. Decide what owns each value and what merely borrows it. Take
&str/&[T]parameters rather than&String/&Vec<T>; return owned values rather than dangling references; acceptimpl Into<String>or generic borrows where it improves the API. Reach forclone()only when a borrow genuinely cannot satisfy the lifetimes — never to silence the borrow checker. Seereferences/ownership-borrowing.md. - Handle errors with
Resultand?, not panics. Library code returnsResult<T, E>/Option<T>; it does notunwrap,expect, orpanic!on conditions a caller could hit. Define a custom error type (thiserrorfor libraries,anyhowfor applications) and let?propagate withFromconversions. Reserve panics for true bugs (broken invariants), and preferexpect("invariant: ...")overunwrap()when you must. Seereferences/error-handling.md. - Prefer iterators and combinators over manual loops and indexing. Express transformations as lazy adapter chains (
iter().filter().map().collect()); usecollect::<Result<_, _>>()to short-circuit fallible work; use theentryAPI for map updates. Index into slices only when the algorithm truly needs positions. Seereferences/collections-iterators.md. - Use traits, generics, and lifetimes idiomatically. Bound generics with the traits they need; prefer static dispatch, reaching for
dynonly for heterogeneous collections or to shrink code size; implementFrom/TryFromfor conversions; lean on elision and add explicit lifetimes only when required. Seereferences/traits-generics-lifetimes.md. - Run
cargo fmt, thencargo clippy -- -D warnings, and fix every finding. Do not suppress lints with#[allow(...)]unless you can justify it in a comment. Clippy's suggestions are idiom guidance — apply them, do not silence them. - Write tests. Add
#[cfg(test)]unit tests for logic and edge cases, integration tests for public behavior, and doc tests for examples. Cover the error paths, not just the happy path. Seereferences/testing.md.
- No needless
clone()— clone only when ownership is genuinely required, never to dodge the borrow checker. - No
unwrap()/expect()/panic!in library code on caller-reachable conditions; propagate with?. Panics are for bugs only. - Never swallow errors (
let _ = result;, emptymatcharms,.ok()to discard). Propagate or handle them meaningfully. - No
unsafewithout a// SAFETY:comment documenting the invariant that makes it sound, and the encapsulation that upholds it. - Make invalid states unrepresentable; do not validate at runtime what the type system can forbid at compile time.
- Code must pass
cargo clippy -- -D warningswith zero#[allow]escape hatches that lack a written justification. - Prefer borrowing over owning in signatures (
&str,&[T],impl AsRef<_>); return owned data, never references to locals. - Follow standard naming:
snake_caseitems,CamelCasetypes,SCREAMING_SNAKE_CASEconsts,new/with_*/try_*constructors.
Open the relevant file on demand — read it when the task touches its area, not upfront.
| Task / trigger | Reference |
|---|---|
| Moves, borrows, lifetimes, clone avoidance, slices, thread closures | references/ownership-borrowing.md |
Result/Option, ?, custom errors, thiserror/anyhow, panics, CLI exit codes |
references/error-handling.md |
Enums/structs, newtypes, typestate, making invalid states unrepresentable, builders, derives, Default |
references/data-modeling.md |
Generics, trait bounds, associated types, static vs dyn dispatch, impl Trait, conversions |
references/traits-generics-lifetimes.md |
Iteration, collect, entry API, Vec capacity, UTF-8-safe String handling |
references/collections-iterators.md |
| Cargo, dependency versions, modules, visibility, re-exports, semver, features, workspaces, CI | references/project-structure.md |
Box/Rc/Arc/Weak, RefCell/Cell, Deref, Drop/RAII |
references/smart-pointers.md |
Threads, channels, Arc<Mutex<_>>, atomics/ordering, thread pools, Send/Sync |
references/concurrency.md |
Test placement, assertions, panic/Result tests, integration/doc tests, TDD, fuzzing |
references/testing.md |
| Naming/casing, constructors, receiver choice, borrowed args, type aliases, operator overloading | references/api-design-naming.md |
unsafe contracts, raw pointers, transmute, repr, FFI, calling/exposing C, no_std |
references/unsafe-ffi.md |
| Bindings/mutability, expression style, control flow, closures, imports, conversions, macros, ranges | references/idioms-antipatterns.md |
| Network/security tooling: async scanners, bounded concurrency, timeouts/rate limits, untrusted-input parsing, secrets handling, TLS, static binaries, CLIs | references/security-tooling.md |
You are not done until all three pass cleanly. Run them and fix every finding before claiming completion — do not report success on unverified code.
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo testOr run the bundled gate: scripts/check.sh (POSIX) / scripts/check.ps1 (Windows) — it runs all three and stops on the first failure.
If formatting differs, run cargo fmt and re-check. If clippy reports lints, fix the code (do not blanket-allow). If tests fail, fix the implementation or the test, then re-run the full gate. Iterate until all three are green.