plugins/language-pro/skills/rust-pro/SKILL.md
--- name: rust-pro description: "Boring Rust" — clone freely, prefer for loops over iterator chains, strict lints, ownership-honest code that compiles and reads cleanly. Use when implementing, debugging, refactoring, or reviewing Rust code; resolving borrow checker errors; tuning Cargo lints; choosing between Arc/Rc/Box; designing trait boundaries; or evaluating whether a clone is the right call. Applies to any Rust work unless a more specific role overrides. --- # Rust Pro Senior-level Rust e
npx skillsauth add rbergman/dark-matter-marketplace plugins/language-pro/skills/rust-proInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Senior-level Rust expertise following "Boring Rust" principles. Correctness over cleverness. One way to do things. Local reasoning.
Cargo.toml, clippy.toml, and rustfmt.toml for project conventionsRequired:
unwrap() or expect() in production code — use .context("...")?unsafe without #[human_authored] designation_ on enums you controlFoundational Principles:
All agent-generated code. Maximum guardrails.
// Complexity limits enforced:
// - Cognitive complexity: 15 max
// - Function lines: 50 max
// - Arguments: 5 max
// Error handling: Always with context
let config = load_config(path)
.context("failed to load configuration")?;
// Iteration: for loops by default (not iterator chains)
for item in collection {
process(item)?;
}
// Matching: Exhaustive, no wildcards
match state {
State::Active => handle_active()?,
State::Pending => handle_pending()?,
State::Done => handle_done()?,
// NO: _ => unreachable!()
}
#[hot_path] (Relaxed)Performance-critical code. Flagged for human review.
#[hot_path]
pub fn process_batch(records: &[Record]) -> Result<Summary, Error> {
// Allowed: iterators, borrowing, fewer clones
records.iter()
.filter(|r| r.is_valid())
.try_fold(Summary::default(), |mut acc, r| {
acc.add(r)?;
Ok(acc)
})
}
Relaxations: Cognitive complexity 20, function lines 75, iterator chains allowed.
#[human_authored] (Unrestricted)Agent cannot modify, only call. For unsafe, SIMD, complex generics.
#[human_authored]
pub fn simd_normalize(vectors: &mut [f32x8]) {
// Agent treats as black box
}
Pin Rust toolchain with mise: mise use [email protected] (creates .mise.toml — commit it, complements rustup). Team members run mise install. See mise skill for setup.
Alternatively, use rust-toolchain.toml (rustup-native) if you prefer not to add mise as a dependency.
# Initialize
cargo new project-name && cd project-name
# Copy configs from this skill's references/ directory:
# references/gitignore → .gitignore
# references/clippy.toml → clippy.toml
# references/cargo_lints.toml → merge into Cargo.toml [lints] section
# references/rustfmt.toml → rustfmt.toml
# For build system, invoke just-pro skill
# Verify
just check # Or: cargo clippy && cargo test
git clone <repo> && cd <repo>
just setup # Runs mise trust/install + cargo build
just check # Verify everything works
Or manually:
mise trust && mise install # Get pinned Rust toolchain
cargo build # Get dependencies
Why Boring Rust? Agent-generated code that compiles is usually correct. Complex patterns cause agents to produce incorrect or unmaintainable code.
Invoke the just-pro skill for build system setup. It covers:
Why just? Consistent toolchain frontend between agents and humans.
Auto-Fix First:
just fix # Or: cargo clippy --fix && cargo fmt
Verification:
just check # Or: cargo clippy --all-targets -- -D warnings && cargo test
Use --all-targets to lint tests, examples, and benches too.
// Libraries: thiserror for typed errors
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("missing field: {field}")]
MissingField { field: &'static str },
#[error("failed to read file")]
Io(#[from] std::io::Error),
}
// Applications: anyhow with context
pub fn load_config(path: &Path) -> anyhow::Result<Config> {
let content = fs::read_to_string(path)
.context("failed to read config file")?;
toml::from_str(&content)
.context("failed to parse config")
}
// Option handling: explicit, never silent
let user = users.get(&id)
.ok_or_else(|| Error::NotFound { id: id.clone() })?;
pub enum ConnectionState {
Disconnected,
Connecting { attempt: u32, started: Instant },
Connected { session: Session },
}
impl ConnectionState {
pub fn connect(&mut self) -> Result<(), Error> {
match self {
Self::Disconnected => {
*self = Self::Connecting {
attempt: 1,
started: Instant::now(),
};
Ok(())
}
Self::Connecting { .. } => Err(Error::AlreadyConnecting),
Self::Connected { .. } => Err(Error::AlreadyConnected),
}
}
}
use bon::Builder;
#[derive(Debug, Builder)]
pub struct ServerConfig {
#[builder(default = 8080)]
port: u16,
host: String, // Required
#[builder(default)]
timeout: Option<Duration>,
}
let config = ServerConfig::builder()
.host("localhost".to_string())
.build();
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserId(String);
impl UserId {
pub fn new(raw: impl Into<String>) -> Result<Self, ValidationError> {
let s = raw.into();
if s.is_empty() {
return Err(ValidationError::Empty("user_id"));
}
Ok(Self(s))
}
pub fn as_str(&self) -> &str { &self.0 }
}
// GOOD: Owned data in, owned data out
pub async fn fetch_user(client: &Client, id: UserId) -> Result<User, Error> {
let response = client
.get(format!("/users/{}", id.as_str()))
.send()
.await
.context("request failed")?;
response.json::<User>().await
.context("failed to parse response")
}
// GOOD: Structured concurrency
pub async fn fetch_all(client: &Client, ids: Vec<UserId>) -> Result<Vec<User>, Error> {
futures::future::try_join_all(
ids.into_iter().map(|id| fetch_user(client, id))
).await
}
// BANNED: Complex lifetime bounds in async
async fn bad<'a>(data: &'a [u8]) -> &'a str { ... }
// BANNED: select!, manual Poll
// src/parser.rs - production code only, keeps file small
#[cfg(test)]
#[path = "parser_tests.rs"]
mod tests;
// src/parser_tests.rs - can have test relaxations
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn test_parser() {
let result = parse("input").unwrap();
assert_eq!(result, expected);
}
project/
├── src/
│ ├── lib.rs # Crate root
│ ├── error.rs # Error types
│ ├── config.rs # Production code
│ ├── config_tests.rs # Tests (if config.rs > 200 lines)
│ └── external/ # Wrappers around external crates
├── Cargo.toml
├── clippy.toml
├── rustfmt.toml
└── justfile
File size targets: Production < 300 LOC (code, excluding comments), Tests < 500 LOC.
These limits exist to improve code architecture, not to be gamed. When a file or function exceeds its clippy/size limit, the correct response is to decompose by responsibility.
Extract, don't compress:
order.rs → order/validate.rs, order/transform.rs) or a sibling moduleWhen extraction is costly: Many locals to pass — consider a context struct or builder pattern.
Prohibited responses to limit violations: combining statements onto single lines, removing or shortening comments, compressing whitespace, shortening descriptive names, inlining helpers. The goal is clean architecture, not metric compliance.
| Banned | Why | Alternative |
|--------|-----|-------------|
| .unwrap() | Panics | .context("...")? |
| .expect("msg") | Panics | .context("msg")? |
| array[i] | Panics | .get(i).ok_or(Error::Index)? |
| unsafe { } | Correctness | #[human_authored] module |
| impl Trait in params | Hides types | <T: Trait> explicit |
| macro_rules! | Complexity | Functions or generics |
| RefCell<T> | Runtime borrow | Restructure with &mut |
| Complex lifetimes | Agent confusion | Clone or restructure |
| select! | Cancellation bugs | Structured concurrency |
| Wildcard _ match | Silent failures | Explicit variants |
| Iterator chains (Tier 1) | Harder to debug | for loops |
clone() to silence borrow checker without understanding why#[allow(...)] without // JUSTIFICATION: commentRefCell, Cell) in agent code| Category | Crate | Notes |
|----------|-------|-------|
| Errors (lib) | thiserror | Derive-based |
| Errors (app) | anyhow | With .context() |
| Builder | bon | Derive-based |
| Serialization | serde | Standard |
| Async runtime | tokio | Blessed subset only |
| HTTP client | reqwest | High-level |
| Logging | tracing | Structured |
| CLI | clap | Derive mode |
Before writing code:
Cargo.toml for dependencies and lint configurationclippy.toml for complexity thresholdsWhen writing code:
.context("what you were doing")?for loops, not iterator chains (unless #[hot_path])_ on your own enumsBefore committing:
just check (standard for projects using just)cargo clippy -- -D warnings && cargo test#[allow] without justification commentClippy and rustfmt walk up directory trees looking for config files. A rogue config in a parent directory (like /tmp) can break your project.
Symptoms:
unknown field errors from clippyFix: Create project-local configs to prevent inheritance:
# clippy.toml - prevents inheriting parent configs
# (empty file is valid)
# rustfmt.toml - minimal stable config
edition = "2024"
cargo init now defaults to edition 2024. If referencing older templates, update them.
references/clippy.toml — Boring Rust clippy configurationreferences/cargo_lints.toml — Cargo.toml [lints] sectionreferences/rustfmt.toml — Formatting rulesreferences/patterns.md — Additional Rust patternsreferences/bevy.md — Bevy ECS patterns (game development)development
Initialize a new repository with standard scaffolding - git, gitignore, AGENTS.md, justfile, mise, beads, and timbers. Use when starting a new project or setting up an existing repo for Claude Code workflows.
data-ai
Activate at session start when using Agent Teams for complex multi-agent work. Establishes team lead role with delegation protocols, teammate spawning, model selection, and beads integration. You coordinate the team; teammates implement.
data-ai
Use when creating a worktree, setting up a worktree, starting feature work that needs isolation, or before executing implementation plans. Covers git worktree creation under .worktrees/, gitignore setup, beads integration, and merge guardrails.
data-ai
Activate when you are a delegated subagent (not the orchestrator). Establishes subagent protocol with terse returns, details to history/, file ownership boundaries, and escalation rules. You implement; orchestrator reviews and commits.