rust
Skill49 starsChanged 6 months agoMITView on GitHub
Mental-model reset for Rust. Use when writing or reviewing Rust code to shift from "it compiles" to "thinks in Rust." Triggers on Rust code review, "is this idiomatic", borrow-checker errors, API design, domain modeling, ownership, lifetimes, errors, traits, async/Tokio, unsafe, serde, FFI, tests, performance, Cargo structure, .rs files, Cargo.toml, rustc diagnostics, clippy findings, Result/Option, thiserror vs anyhow, newtype, typestate, enum vs trait, dyn Trait, Send/Sync, Pin, Miri, PyO3, napi-rs, cxx, UniFFI, wasm-bindgen, serde attributes, or feature unification.
38 other projects use a version of this.
Adopted by
Most recently taken up by
- elodin-sys/elodintheir own version, first seen Nov 9, 2026
- crbnos/carbontheir own version, first seen Nov 7, 2026
- lawless-m/claude-skillstheir own version, first seen Nov 6, 2026
- inference-gateway/skillstheir own version, first seen Nov 5, 2026
- alexjmiller5/agent-config-publictheir own version, first seen Nov 1, 2026
- its-ash/skillstheir own version, first seen Nov 1, 2026
Used with
Projects with it also use java, node and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
testdino-hqSkilljava
Google's official Java style guide. Covers 2-space indentation, 100-char line limit, naming conventions, braces, imports, Javadoc, exception handling, lambdas, and streams. Enforces @Override annotations and specific imports.
Also in 4 projects that use this
mcollinaSkillnode
Provides domain-specific best practices for Node.js development with TypeScript, covering type stripping, async patterns, error handling, streams, modules, testing, performance, caching, logging, and more. Use when setting up Node.js projects with native TypeScript support, configuring type stripping (--experimental-strip-types), writing Node 22+ TypeScript without a build step, or when the user mentions 'native TypeScript in Node', 'strip types', 'Node 22 TypeScript', '.ts files without compilation', 'ts-node alternative', or needs guidance on error handling, graceful shutdown, flaky tests, profiling, or environment configuration in Node.js. Helps configure tsconfig.json for type stripping, set up package.json scripts, handle module resolution and import extensions, and apply robust patterns across the full Node.js stack.
Also in 3 projects that use this
rogue-dev-studioSkillpython
Python 3.13 with uv package manager via mise. Use when the project needs this capability or the user / team manifest asks for it. Use for specialized `python` work when listed in TEAM.yaml or explicitly requested.
Also in 6 projects that use this
rogue-dev-studioSkillgolang
Go development environment. Use when the project needs this capability or the user / team manifest asks for it. Use for specialized `golang` work when listed in TEAM.yaml or explicitly requested.
Also in 3 projects that use this
trotsky1997Skillast-grep
Guide for writing ast-grep rules to perform structural code search and analysis. Use when users need to search codebases using Abstract Syntax Tree (AST) patterns, find specific code structures, or perform complex code queries that go beyond simple text search. This skill should be used when users ask to search for code patterns, find specific language constructs, or locate code with particular structural characteristics.
Also in 3 projects that use this
OpenAISkillnextjs
Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.
Also in 3 projects that use this
Other versions38 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 38
- ccusage/ccusage★ 19ksame name
- ryoppippi/ccusage★ 19ksame name
- microsoft/fast★ 9.7ksame name
- pgdogdev/pgdog★ 5.6ksame name
- crbnos/carbon★ 2.8ksame name
- elodin-sys/elodin★ 553same name
- Mindrally/skills★ 275same name
- Moosync/Moosync★ 264same name
- wado-lang/wado★ 120same name
- databricks-solutions/apx★ 91same name
28 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository26 other files it gives its agents.
- AGENTS.mdAGENTS.md
- coding-standardsSkill
- coolify-composeSkill
- crafting-effective-readmesSkill
- diataxisSkill
- english-pleaseSkill
- feature-planning-artifactsSkill
- frontend-design-principlesSkill
- grug-brained-devSkill
- html-artifactsSkill
- improveSkill
- improving-promptsSkill
- jjSkill
- reducing-entropySkill
- researching-codebasesSkill
- roadmapSkill
- roadmap-to-improve-plansSkill
- salsaSkill
- skill-authoringSkill
- strategic-roadmapSkill
- svelte5Skill
- sveltekitSkill
- writing-cli-skillsSkill
- writing-error-messagesSkill
- writing-plansSkill
- writingSkill
The file
We split this skill into the rules it gives an agent
5 rules. 0 are common, 2 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 31 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 22
When reviewing Rust, start with the shape of the program: what invariants are represented, who owns each value, which states are …
- Distinctive: line 24
Treat these as strong defaults, not rigid laws: when unsure, choose the approach that moves invariants into types and lets the compiler …
- Very specific: line 37
Closed sets are enums, not trait objects: If you know all variants at compile time, use an enum: zero-cost dispatch, exhaustive matching, …
- Very specific: line 46
Async is for waiting, not for CPU work: Never block the runtime. Use async I/O, spawn_blocking for short blocking calls, and Rayon or …
- Very specific: line 57
Public fields beat trivial getters: If any value of a field's type is valid, make it pub.
“Common” is measured: kept by at least 6 in 10 of the 31 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Thinking in Rust
You already know Rust syntax. Change the defaults you reach for first when modeling a domain, handling ownership, designing APIs, or crossing boundaries.
The core failure mode: writing Rust that compiles but thinks like Python, Java, TypeScript, or C. Bare String for domain types. bool for states. Trait objects for closed sets. Error(String) for everything. _ => in every match. Index loops. Sentinel values. Getters and setters on every field. clone() to quiet the compiler. unsafe to escape design pressure. These compile. They are wrong.
Most of these habits come from languages without sum types, ownership, zero-cost newtypes, or exhaustive matching. Recognizing where a pattern comes from helps you see why it is wrong in Rust.
When reviewing Rust, start with the shape of the program: what invariants are represented, who owns each value, which states are impossible, where errors cross boundaries, and whether any escape hatch is hiding a design problem.
Treat these as strong defaults, not rigid laws: when unsure, choose the approach that moves invariants into types and lets the compiler enforce them.
How Rust Thinks
Model the domain in types
- Every string with domain meaning is a newtype. Bare
Stringerases domain knowledge. The compiler cannot distinguish an email from a username from a URL. Wrap it, validate at construction, keep the field private. See references/newtypes-and-domain-types.md. - Every boolean parameter is a lie — use an enum.
trueandfalsecarry no meaning at the call site and cannot extend to a third state. Replace flags with named variants. Applies to struct fields too: correlated booleans are a state machine in disguise. See references/bool-to-enum.md. - Every "I don't know" is explicit.
Option<bool>has three states but none of them are named. Empty collections can mean "checked and empty" or "not checked yet." Make each state a named variant. See references/option-bool-to-enum.md. - Every match on your enum is exhaustive — no wildcard
_ =>arms. Wildcards silence the compiler when you add variants. List every variant of enums you control._ =>is for foreign#[non_exhaustive]types and primitives. See references/exhaustive-matching.md. - Every error variant is a domain fact — no
Error(String). String errors throw away structure. Callers cannot match, test, retry, translate, or recover. Libraries expose typed error enums; applications add context. See error-handling.md. - Parse, don't validate. Validation checks data and throws away the proof. Parsing checks data and returns a type that proves the invariant. After parsing, do not re-check downstream. See references/parse-dont-validate.md.
- Enums are the primary modeling tool. Rust enums are sum types. A struct with a
kindfield plusOptionpayload fields is always an enum waiting to be written. See references/enums-as-modeling-tool.md. - Closed sets are enums, not trait objects. If you know all variants at compile time, use an enum: zero-cost dispatch, exhaustive matching, per-variant data. Use generics or
dyn Traitonly when the set is genuinely open. See traits.md. - Boundaries translate; internals model. Serde, FFI, CLI, HTTP, and database edges should convert DTOs into domain types. Do not let wire formats become your internal model. See serde.md and interop.md.
Express ownership and API intent
- Borrow by default — own when intentional. Accept
&str,&[T], and&Pathunless you need to store, mutate, transform, or transfer ownership. See references/borrow-by-default.md. - Function signatures are ownership contracts. A signature should reveal who owns, who borrows, who mutates, and how long values remain valid. If the signature lies, the borrow checker will make you pay. See references/function-signatures.md.
- Clone is not a design tool. Clone for independent ownership, thread transfer, or stored copies. Do not clone because E0382 made you sad. See ownership.md.
- Restructure ownership before
Rc<RefCell<T>>.RefCelltrades compile-time borrow checking for runtime panics. First try split borrows, read-then-write phases, arenas/indices, or explicit ownership flow. See references/ownership-before-refcell.md. - Async is for waiting, not for CPU work. Never block the runtime. Use async I/O,
spawn_blockingfor short blocking calls, and Rayon or dedicated threads for CPU-bound work. See async.md. - Unsafe and atomics require written proofs. Atomics need a small ordering argument. Unsafe needs the smallest safe wrapper,
# Safetydocs,// SAFETY:comments, and Miri when validity or aliasing matters. See atomics.md and unsafe.md.
Express intent in APIs and control flow
- Iterators over index loops.
for i in 0..v.len()risks off-by-one errors and obscures intent. Use.iter(),.enumerate(),.windows(),.zip(), and adapters that say what you mean. See references/iterators-over-indexing.md. Optionover sentinel values.-1,"",0, andu32::MAXas "no value" markers are invisible to the type system. UseOption<T>. See references/option-over-sentinels.md.- One entity, one struct — not parallel collections. Multiple maps or vectors sharing keys depend on discipline, not types. Group related data into a struct and store one collection of that entity. See references/struct-collections.md.
- Transform over mutate. For configuration and construction, prefer consuming
selfchains over&mut selfsetters. Reserve&mut selffor live objects being operated on. See references/transform-over-mutate.md. - Modules are namespaces, not
implblocks. A unit struct with only associated functions is a Java class in disguise. Use modules for free functions; use traits when method syntax matters. See references/impl-namespace.md. - Right-size pattern matching.
matches!()for boolean checks.if letfor one variant.let ... elsefor early return.matchfor real alternatives. Exhaustivematchwhen adding a variant should break the build. See references/pattern-matching-tools.md. - Public fields beat trivial getters. If any value of a field's type is valid, make it
pub. Do not writeget_x()/set_x()that only forwards. When accessors protect invariants, use Rust naming:name(), notget_name(). See references/getter-setter.md. - Visibility is a design tool, not cleanup.
pubis a semver promise. Default to private orpub(crate), group modules by domain, and curate the public facade withpub use. See references/visibility-and-modules.md. - Crate boundaries must earn their names. Stay single-crate until you can name the boundary. Cargo features are additive public capability, not an internal architecture switch. See project-structure.md.
Common Mistakes (Agent Failure Modes)
- Public newtype fields (
pub struct Email(pub String)) → Make the field private; force construction throughparse/newso invariants cannot be bypassed. - Boolean flags leaking into APIs → Replace with enums, even when there are only two states today.
Option<bool>or nestedOptionstate → Name the states. Stop making callers decode truth tables.kindfield plusOptionpayload fields → Replace with an enum carrying per-variant data; delete the impossible states.- Wildcard matches on your own enums → List every variant; adding a variant should break the build.
- Validation that returns
Result<(), E>and then forgets the proof → Parse once at the boundary into a domain type. Error(String)or a crate-wide error blob → Define structured errors for one unit of fallibility.anyhow::Errorin a public library API → Use a library error type; reserveanyhowfor binaries/apps.- Bare
?in application code → Add.context()so the error says what you were doing. - Taking ownership by default → Borrow unless you store, return, transform, or transfer ownership.
clone()as first response to E0382 → Ask who should own the data. Clone only when you can name why.'staticadded to silence a lifetime error → Fix the relationship between lifetimes; do not make your API less useful.&String,&Vec<T>, or&PathBufin APIs → Accept&str,&[T], or&Path.Rc<RefCell<T>>orArc<Mutex<T>>as first resort → Restructure ownership or use message passing.dyn Traitfor a closed set → Use an enum. Interfaces are not free flexibility in Rust.- Blocking or CPU work inside
async fn→ Use async APIs,spawn_blocking, or Rayon/thread pool. - Holding a lock guard across
.await→ Narrow the lock scope or redesign shared state. Ordering::Relaxedbecause it is faster → Write the proof; otherwise use Release/Acquire orSeqCst.- Unsafe to dodge the borrow checker → The pattern is probably wrong. Restructure first.
serde_json::Valueas the internal model → Use DTOs at the boundary and domain types inside.- Benchmarking debug builds or optimizing cold code → Measure
--releasefirst; keep invariants until profiling proves otherwise. - Feature flags for internal workspace architecture → Use modules/crates; features are additive public capability.
Review Checklist
- Domain primitive? → Newtype, enum, or parser-backed type.
- Boolean or
Option<bool>state? → Named enum variants. - Wildcard match on owned enum? → Exhaustive match.
- Validation repeated downstream? → Parse once at the boundary.
- Borrow checker appeased with
clone(),'static,Rc<RefCell<_>>, or unsafe? → Rework ownership first. - Public signature takes owned data but only reads? → Borrow
&str,&[T], or&Path. - Library returns stringly or
anyhowerrors? → Structured public error type. - Polymorphism unclear? → Enum, then generics, then
dynonly for true erasure. - Async code blocks, holds locks across
.await, or fans out unboundedly? → Move blocking work and bound concurrency. - Unsafe or atomics present? → Check the written invariant/proof and run Miri when relevant.
- Serde/FFI/API boundary leaks into internals? → Translate DTOs into domain types.
- Performance concern? → Measure
--releasebefore cleverness. - Everything is
pubor feature-gated internally? → Curate the facade; keep features additive.
Quick Reference
| Code smell | Rust default move | Reference |
Bare String/u64 for domain values | Newtype with private field | references/newtypes-and-domain-types.md |
bool parameter or state field | Two-variant enum | references/bool-to-enum.md |
Option<bool> / nested Option | Named enum variants | references/option-bool-to-enum.md |
_ => on your own enum | List every variant | references/exhaustive-matching.md |
Error(String) in a library | Typed error enum scoped to the operation | error-handling.md |
| Validate then forget | Parse into a domain type | references/parse-dont-validate.md |
kind field + Option payloads | Enum with per-variant data | references/enums-as-modeling-tool.md |
Box<dyn Trait> for a closed set | Enum, or generics if the set is open | traits.md |
Function takes Vec<T>/String but only reads | Borrow &[T] / &str | references/borrow-by-default.md |
| Defensive clone or lifetime fight | Redesign ownership before adding escape hatches | ownership.md |
for i in 0..v.len() | Iterator chain | references/iterators-over-indexing.md |
| Magic number/string for absence | Option<T> | references/option-over-sentinels.md |
| Parallel collections with shared keys | Single collection of structs | references/struct-collections.md |
&mut self builder setters | Consuming self chains | references/transform-over-mutate.md |
| Unit struct with only associated functions | Module with free functions | references/impl-namespace.md |
One meaningful match arm | if let, let ... else, or matches! | references/pattern-matching-tools.md |
Trivial get_x() / set_x() | Public field or x() accessor with invariant | references/getter-setter.md |
Everything pub / one giant file | Modules plus curated visibility | references/visibility-and-modules.md |
| Blocking work or unbounded fan-out in async code | Async waits; CPU blocks elsewhere; bound everything | async.md |
| Atomic ordering chosen by vibe | Use atomics only with a written proof | atomics.md |
| Unsafe added to bypass compiler friction | Isolate unsafe and document the invariant | unsafe.md |
| Wire format leaking into internals | Translate DTOs into domain types | serde.md |
| FFI or host-runtime boundary | Keep the ABI small, typed, and panic-safe | interop.md |
| Crate/workspace/API shape unclear | Stay single-crate until the boundary has a name | project-structure.md |
Cross-References
- ownership.md — Borrow checker errors, lifetimes, function signatures, smart pointers,
Cow, clone discipline. - error-handling.md —
thiserrorvsanyhow, structured errors, context, combinators, panic boundaries. - traits.md and type-design.md — Dispatch choices, trait design, newtypes, typestate, builders, phantom types.
- async.md, atomics.md, and unsafe.md — Concurrency, memory ordering, soundness, Miri,
Send/Syncinvariants. - macros.md, testing.md, and performance.md — Generated code, validation strategy, profiling-first optimization.
- serde.md, interop.md, and project-structure.md — Boundaries, DTOs, FFI, workspaces, features, public API surface.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
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.

