Install
$ agentstack add skill-xobotyi-cc-foundry-rust ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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.mdborrowing/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.mdResult/Option/?,thiserrorenum templates,
anyhow .context(), the panic/unwrap/expect policy, error-type design (Display/Error/Send/Sync)
- Anti-patterns —
${CLAUDE_SKILL_DIR}/references/anti-patterns.mdAI-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.mdthe Rust API GuidelinesC-*checklist, RFC 430
naming, #[non_exhaustive], #[must_use], common-trait derives, sealed traits, builders
- Parallelism —
${CLAUDE_SKILL_DIR}/references/parallelism.mdthreads, scoped threads, rayon,Send/Sync,
channels, shared state (Arc/Mutex) — the CPU-bound multicore path
- Async —
${CLAUDE_SKILL_DIR}/references/async.mdtheFuture/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.mdmod.rs-free layout, cargo workspaces, the
[lints]/[workspace.lints] table, profiles, features, MSRV
- Toolchain —
${CLAUDE_SKILL_DIR}/references/toolchain.mdthe fmt/clippy/nextest/deny verification gates,
rust-analyzer configuration, recommended compiler and Clippy lint sets
- Edition 2024 —
${CLAUDE_SKILL_DIR}/references/edition-2024.mdRust 2024 edition specifics and migration via
cargo fix --edition
- Documentation —
${CLAUDE_SKILL_DIR}/references/documentation.mdrustdoc conventions, doctests,missing_docs,
intra-doc links, semver-checks
Naming
- Casing follows RFC 430:
UpperCamelCasefor types/traits/enum variants,snake_casefor functions/methods/modules,
SCREAMING_SNAKE_CASE for consts/statics.
- Acronyms are one word in
UpperCamelCase(Uuid,Stdin, notUUID,StdIn); lower-cased insnake_case. - Getters omit the
get_prefix:fn first(&self) -> &First, notfn get_first. Usegetonly 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, matchingParseIntError). 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
Builderis Rust's name for a factory. Acceptimpl Fn() -> Foorather than aFooBuilderparameter.
Ownership and Borrowing
- Accept the least-owning type that does the job:
&strover&String,&[T]over&Vec,&ToverTwhen 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/Arconly 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/&Tover 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; absence-not-error returnsOption. 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 areSend + Sync + 'staticso they cross threads
and compose with anyhow/Box.
.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
NonEmptyVecor a parsedEmailnewtype
beats a runtime check on a raw Vec/String.
Traits and Generics
- Static dispatch is the default: generics (
fn f) orimpl Traitin argument and return position.
Monomorphization keeps it allocation-free and inlinable.
- Use
dyn Traitonly for genuine runtime polymorphism — heterogeneous collections (Vec>), plugin
boundaries, or breaking deep monomorphization. Always write dyn explicitly (Box, &dyn Handler); bare-trait-object syntax has been an error since edition 2021.
impl Traitin 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:
Debugon every public type, plusClone,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 givesIntofree),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 &collectionoverfor i in 0..collection.len(); index only when the index itself is the data. matcharms 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 letfor single-pattern matches, andlet ... 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 writing any concurrent code — this decision is not interchangeable:
- CPU-bound / multicore compute (parsing, hashing, image processing, number crunching) -> OS threads or rayon
(par_iter). Never reach for async here — async provides zero parallel speedup for CPU work; it only interleaves tasks at .await points on (often) a single logical flow.
- Many concurrent I/O operations (thousands of sockets, requests, file handles) -> async / Tokio. Async wins by
not blocking a thread while waiting on I/O, letting one runtime juggle huge numbers of in-flight operations.
- A handful of blocking I/O tasks -> plain threads are simpler than dragging in an async runtime; use async only
when the concurrency count makes a thread-per-task model wasteful.
Further rules once the model is chosen:
- Shared state crosses threads via
Arc>/Arc>; immutable shared data viaArc. Move work and
ownership across threads with channels (std::sync::mpsc, crossbeam, or tokio::sync in async).
- Public types should be
Send(and usuallySync) so they work under Tokio and behind runtime abstractions. A!Send
value held across an .await makes the whole future !Send — keep Rc/RefCell out of async scopes that span an await.
- Never block in async: no
std::thread::sleep, no synchronous file/network I/O, no long CPU loops on the runtime.
Offload CPU work with tokio::task::spawn_blocking or a rayon pool, and use the runtime's async timers/IO.
- The borrow checker does not see deadlocks or blocking-in-async — those footguns are invisible at compile time. Read
references/async.md before writing async code.
Project Structure
- Use the
mod.rs-free layout: a modulefoolives infoo.rs, its submodules infoo/bar.rs. Never create new
mod.rs files.
- Set all lint levels in the
[lints](or[workspace.lints]) table inCargo.toml, not in scattered#![deny(...)]
attributes or RUSTFLAGS. This keeps lint policy in one auditable place and inherited across the workspace.
- Override a project-global lint at a narrow scope with
#[expect(lint, reason = "...")], not#[allow]—expect
warns when the lint stops firing, preventing stale overrides. (#[allow] is acceptable on generated code/macros.)
- Multi-crate projects use a cargo workspace with a shared
[workspace.lints]and[workspace.dependencies]. Split a
crate when a submodule is independently useful — many small crates compile faster and prevent dependency cycles.
- Keep
unsafeconfined to the smallest possible module; soundness boundaries are module boundaries. Everyunsafe
block carries a // SAFETY: comment justifying it. Never write sound-looking safe wrappers over unsound assumptions.
Toolchain Gates
Code is not done until these pass (run via Bash, surfacing each command):
cargo fmt --check— formatting is non-negotiable; never hand-format.cargo clippy --all-targets --all-features -- -D warnings— Clippy clean across the whole build graph, warnings as
errors. Fix the cause; suppress a lint only with a scoped #[expect(..., reason = "...")].
cargo nextest runandcargo test --doc— nextest is the fast test runner but does not execute doctests, so
the separate --doc run is mandatory to keep documentation examples honest.
cargo deny check(orcargo audit) — gate dependencies for advisories, license, and source policy.
Doc Comments
//!documents the crate root and modules;///documents items (functions, types, fields, variants). Write docs as
part of the change, not after.
- Use the standard rustdoc sections:
# Examples,# Panics(when the fn can panic),# Errors(what aResult
returns on failure), # Safety (the invariants a caller of an unsafe fn must uphold).
- Doctests use
?for fallible calls (wrap the body so it returnsResult), never.unwrap()— examples are also
documentation of idiomatic usage.
- Libraries enable
#![warn(missing_docs)]so every public item is documented. Link related items with intra-doc links
([Type], [mod::func]).
- When you change an exported symbol's signature or behavior, update its doc comment in the same edit — a stale doc is
worse than none.
Code Navigation (LSP Required)
A rust-analyzer LSP is configured for .rs files. For semantic navigation, use LSP tools — they understand types, traits, and macro expansion in ways text search cannot:
- goToDefinition — jump to where a symbol is defined (through re-exports and macros).
- findReferences — every real use of a symbol, not textual name collisions.
- hover — resolved type, signature, and docs at a position.
- workspaceSymbol — locate a type/fn/trait by name across the workspace.
- goToImplementation — concrete implementors of a trait, or the trait a method satisfies.
- incomingCalls / outgoingCalls — call hierarchy for impact analysis before a change.
Use Grep/Glob only for non-semantic text: comments, string literals, config files, log messages, Cargo.toml entries. Reaching for Grep to find a definition or all callers is a mistake when the LSP resolves it precisely.
Integration
- the-coder owns the language-agnostic workflow — discovery, planning, verification loop. This skill governs
Rust-specific conventions only; defer the workflow mechanics to the-coder and apply these rules within it.
- rust-testing owns the test ecosystem (
#[test]/integration/doctest layout, cargo-nextest, proptest, insta,
criterion, mockall, rstest). When writing or reviewing tests, that skill leads; it relies on this skill for general language conventions and on references/async.md for async-test mechanics.
- When this skill's conventions conflict with an established pattern already pervasive in the codebase, the codebase
wins — follow it and flag the divergence once.
Application
When writing Rust:
- Apply every convention above silently — do not narrate "per RFC 430 I'll name this...". Just write idiomatic code.
- Match the surrounding codebase. If it diverges from these conventions consistently (e.g. uses
mod.rs, a different
error crate), follow the codebase and note the divergence once rather than fighting it file-by-file.
- Run the toolchain gates before declaring the work done.
When reviewing Rust:
- Cite the specific violation (file:line and the rule), then show the corrected code inline. Do not lecture in the
abstract.
- Prioritize correctness and soundness (unsound
unsafe, blocking-in-async, panics-as-errors, data races) over style
nits; let cargo fmt and Clippy carry the mechanical issues.
- Confirm the concurrency model fits the workload (CPU-bound vs I/O-bound) before reviewing the concurrency details.
The borrow checker is a collaborator, not an adversary. When it pushes back, the answer is almost never .clone(), Rc, or unsafe — it is a clearer ownership structure. Encode invariants in types so wrong code does not compile, keep unsafe rare and justified, and let the toolchain gates (fmt, clippy, nextest + doctests, deny) be the final word on done.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: xobotyi
- Source: xobotyi/cc-foundry
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.