agentsclimarketplace

Clean rust

Skill uwuclxdy/agenticat/skills/clean-rust

Clean, idiomatic Rust 2024 conventions: ownership, error handling, async, unsafe, traits, iterators, performance. Use when writing or reviewing Rust, running clippy, or checking 'idiomatic rust'.From its SKILL.md

Install
npx -y skills add uwuclxdy/agenticat --skill clean-rust

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 2 commands, including `cargo fmt` and 1 more.

SKILL.md

10.1 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

Clean Rust

Rust-specific conventions for writing, reviewing, and refactoring. The core rules below always apply. Load the one reference file matching the task's domain; don't load them all.

If the clean-code skill is installed, its language-agnostic principles (function size, naming hygiene, comment discipline) still apply. Where generic advice conflicts with Rust idiom, this skill wins. Classic examples: "prefer exceptions over error codes" maps to Result, never panics; "replace switch with polymorphism" maps to exhaustive match, which in Rust is a feature, not a smell.

Task touchesFile
Error types, propagation helpers, failure semantics, retriesreferences/error-handling.md
async/await, tokio, select!, channels, spawned child processesreferences/async.md
Threads, atomics, lock ordering, drop orderreferences/concurrency.md
Process-global mutable state (env vars, global overrides) across threadsreferences/edition-2024.md
Public API shape, builders, newtypes, typestate, serdereferences/api-design.md
Any unsafe block, FFI, raw pointers, exported symbolsreferences/unsafe.md
Writing tests or debugging test infrastructurereferences/testing.md
Hot paths, allocations, string building, hasher choicereferences/performance.md
Benchmarks, criterion, #[bench]references/performance.md
Secret files on disk, keys and tokens at restreferences/security.md
Logging, tracing spans, log levelsreferences/observability.md
Edition 2024 migration or 2024-specific behavior changesreferences/edition-2024.md

Errors

  • Result + ? everywhere; no .unwrap()/.expect() outside tests and one-time init of hand-audited literals (LazyLock<Regex> on a fixed pattern). The rare justified expect carries a message naming the invariant that makes it safe.
  • No sentinel returns (-1, "") for failure. Option<T> for absence that isn't an error.
  • thiserror for library errors (callers can match), anyhow/eyre at the application boundary. Never Box<dyn Error> or String as a library error type.
  • Bind a Result once with match/if let/let-else, never .is_ok() followed by separate access or a downstream .unwrap().
  • Pick one failure semantics per operation: best-effort (warn per item, Ok(()) at end) or fail-fast (propagate first error). Never warn-per-item and then fail at the end; that surprises exit-code consumers.

Ownership & Borrowing

  • Borrow by default: &str, &[T], impl AsRef<Path> for read-only params. Never &String or &Vec<T>.
  • Every .clone() is an explicit cost, never one to silence the borrow checker. Cow<'_, str> when allocation is conditional; Arc<T> for shared ownership across threads.
  • Unsigned types for counts, ports, sizes: jobs: u32, not jobs: i32. The type documents the constraint.
  • Let lifetime elision work. A struct with 3+ lifetime params is a design smell: consider owned data.

Naming

RFC 430 casing (snake_case items, CamelCase types, SCREAMING_SNAKE_CASE consts) plus semantic method prefixes:

PrefixContract
into_consumes self, returns owned
as_cheap borrowed view, no allocation
to_possibly expensive, may allocate
try_fallible variant returning Result
is_ / has_boolean query
with_builder-style configuration
_mut suffixmutable variant
  • No get_ for plain field access: fn name(&self) -> &str. Reserve get for fallible lookups (get(key) -> Option<&V>).
  • Concrete names: cmd not executor, typ not ty. Single letters only in tiny scopes.
  • Names must not lie about the data: no _list suffix on a HashMap, no owned_ on a &str.
  • Wrap semantically distinct primitives in newtypes (UserId(u64), OrderId(u64)) so the compiler catches argument swaps.

Control Flow & Iterators

  • match on enums stays exhaustive: no _ => {} arm in dispatchers. Spell out every variant: an empty arm with a comment beats a wildcard that silently swallows the next variant added.
  • let-else for guard-style early returns; it reads linearly where .ok().and_then(...).is_some_and(...) chains don't.
  • Iterator chains for value-producing pipelines. When the result is discarded (let _ = ...), the chain is control flow in disguise; write the for loop.
  • Return impl Iterator<Item = T> when callers consume sequentially; premature .collect() allocates for nothing.
  • Group one logical filter into one .filter_map() closure (using ? and early return None) instead of fragmented .filter_map().filter().filter_map() chains.
  • Don't reimplement the standard library: split_once('=') over .splitn(2, '=').collect(), unwrap_or_default() over unwrap_or_else(|| T::default()).
  • One fluent chain over three named single-use intermediates; declare variables next to first use, not at the top of the function.

Types & Traits

  • Derive liberally where semantically correct: Debug on effectively every public type; Clone, PartialEq, Eq, Hash as meaning allows (if a == b then hash(a) == hash(b) must hold); Default when a meaningful empty value exists.
  • dyn Trait only for genuinely open sets (plugins, user extension). A closed set of a few known types is an enum: simpler and faster.
  • Typed #[derive(Deserialize)] structs over serde_json::Value indexing: field typos become compile errors instead of silent runtime Nones.
  • impl Trait in argument position for simple bounds; where clauses when signatures grow. Don't make a function generic when one concrete type is ever used.

Platform & cfg

  • Short forms: #[cfg(windows)], #[cfg(unix)], not #[cfg(target_os = "windows")].
  • No redundant #[cfg] inside a module that's already cfg-gated at its mod declaration.
  • Helpers used on only one platform must be gated (or #[allow(unused)]) so every target compiles warning-free. Cross-platform CI fails on the platform that doesn't use them.

Strings & IO

  • static RE: LazyLock<Regex> for compiled-once regexes; never compile inside a loop or per call.
  • ASCII character classes ([a-zA-Z0-9]) over POSIX [[:alnum:]]: the explicit range reads unambiguously and ports to engines where POSIX classes are locale-sensitive (in Rust's regex they're ASCII-only either way).
  • Inline format args: debug!("found {name:?}"), not debug!("found {:?}", name).
  • Forward child-process output with io::stdout().write_all(&output.stdout)?; println! mangles encoding and panics on broken pipes.
  • Long-form flags when spawning external commands (--force, not -f): the call site is its own documentation.
  • Never truncate a String at a raw byte index. &s[..n] panics mid-codepoint on non-ASCII; the "safe" s.get(..n).unwrap_or(s) is worse: it returns the whole string when n lands mid-codepoint, so oversize input escapes the cap. Use floor_char_boundary(n) or walk char_indices().

Modules & Visibility

  • Private by default; pub(crate) for internal sharing. Fields with invariants stay private behind methods.
  • Organize by domain (order/, user/), not by kind (models/, services/).
  • pub items before private helpers in a file; use statements at the top, never inside functions.

Comments & Docs

  • Comments explain why: hidden constraints, upstream bug links, invariants the reader can't see. Never what the next line does. Delete commented-out code; git remembers.
  • Public APIs get /// docs with # Examples (runnable), plus # Errors, # Panics, # Safety when applicable.
  • Every lint suppression is justified: #[expect(lint, reason = "...")] over bare #[allow].
  • Non-obvious string input formats get documented above the signature (// accepts "name", "name:tag", or "ns/name:tag").

Tooling

  • cargo fmt and cargo clippy clean in CI, no exceptions. Fix warnings; don't blanket-suppress.
  • Libraries: #![warn(clippy::pedantic)] and selectively allow, never a blanket #![allow(clippy::all)]. Consider unwrap_used/expect_used at warn (see references/testing.md for keeping test code exempt).
  • No formatting-only churn in feature PRs: the diff should match the description.

Unsafe

Default posture: unsafe_code = "forbid" until a concrete need exists. When it does exist, every block carries a // SAFETY: comment and stays minimal; full discipline in references/unsafe.md.

Process

  • One PR = one focused change; formatting fixes, new workflows, and features travel separately.
  • A technically-correct change that breaks a user contract (CLI flags, config keys, env semantics) keeps the legacy path, ships the new one alongside, and milestones the removal for the next major. Warn only on genuine old-vs-new conflicts: users on only-old or only-new see nothing.
  • Feature-detect external tools by parsing --version output; where that's unreliable, sniff for specific stderr messages. Either way, probe with plain .output() so a failed probe stays non-fatal.

Pre-Submit Checklist

  • No .unwrap() outside tests/compile-time constants; no .is_ok() + separate access
  • Failure semantics consistent: best-effort or fail-fast, not mixed; cleanup always runs
  • Borrows by default; no &String/&Vec<T>; every .clone() intentional
  • Method prefixes honest (into_/as_/to_/try_); newtypes for swappable primitives
  • No _ => {} in enum dispatchers; let-else over combinator gymnastics
  • impl Iterator over premature Vec; no stdlib reimplementations
  • Typed Deserialize over Value; dyn only for open sets
  • #[cfg(windows)] short form; single-platform helpers gated for all-target CI
  • LazyLock regexes; inline format args; write_all for child output
  • Comments say why; suppressions carry reason; no commented-out code
  • fmt + clippy clean; no formatting churn outside the change
  • One focused change per PR; breaking changes keep the legacy path, removal milestoned

What ships with it: 10 files

34.5 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.