# Rust Style

> >

- **Type:** Skill
- **Install:** `agentstack add skill-furedea-agent-harness-rust-style`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [furedea](https://agentstack.voostack.com/s/furedea)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [furedea](https://github.com/furedea)
- **Source:** https://github.com/furedea/agent-harness/tree/main/agents/skills/rust-style

## Install

```sh
agentstack add skill-furedea-agent-harness-rust-style
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Rust Coding Style Guidelines

## Scope

This skill governs code written inside an already-bootstrapped Rust project: package commands, module layout, test authoring, refactoring, code review, naming, ownership, errors, filesystem operations, comments, and docs.

Project bootstrap (flake.nix, direnv, initial `Cargo.toml`, initial `rust-toolchain.toml`) belongs to the `nix-dev-init` skill. If the project is not yet bootstrapped, defer to `nix-dev-init` first and return here once `direnv allow` succeeds and Cargo is available.

### Why the split

Bootstrap is an environment concern. This skill is for day-to-day Rust implementation inside an existing project. Do not duplicate Nix, direnv, or initial Cargo setup rules here.

## Package Management

- Use Cargo for Rust package operations.
- Add dependencies with `cargo add ` when `cargo add` is available.
- Add development dependencies with `cargo add --dev `.
- Use `cargo check` for a fast compile/type-check pass when tests are not needed yet.
- Use `cargo test` for the default verification pass.
- Use `cargo clippy --all-targets -- -D warnings` for linting.
- Do not edit `Cargo.lock` manually.
- Commit `Cargo.lock` for applications, CLI tools, and internal tools.
- For library crates, follow the repository's existing `Cargo.lock` policy.
- Do not use `@latest`-style version shortcuts in documentation or committed commands.
- Do not add a dependency only because it is common. Add it when it removes real boundary complexity or prevents error-prone code.

## Directory Structure

- Follow Cargo's standard layout.
- Store production code in `src/`.
- Keep `src/main.rs` thin for binary crates. It should parse CLI arguments and call library code.
- Put reusable execution logic in `src/lib.rs` and focused modules under `src/`.
- Store integration tests in `tests/`.
- Keep unit tests near the module when they need private access.
- Use `examples/` only for runnable examples that should compile.
- Avoid `utils.rs` and `helpers.rs`; name modules by domain or action.

Typical binary crate shape:

```text
src/
├── main.rs
├── lib.rs
├── cli.rs
├── config.rs
├── fs_ops.rs
└── render.rs
tests/
└── cli.rs
```

## File Standards

- Let `rustfmt` define formatting. Do not hand-format around rustfmt.
- Keep files focused and cohesive.
- Prefer modules of roughly 200-500 lines. Split when multiple responsibilities appear.
- Keep public items before private helpers when it improves scanning.
- Use `mod.rs` only when the existing project already uses that style; otherwise prefer `module_name.rs` plus `module_name/child.rs`.
- Put one top-level concept per file when the concept has real behavior.
- Keep generated code out of hand-written modules unless the project has a clear generated-code convention.

## Testing

- Use `cargo test` as the default test command.
- Write unit tests in the target source file or module with `#[cfg(test)] mod tests`.
- Use `tests/*.rs` for integration tests that exercise public APIs or CLI behavior.
- Integration tests compile as a separate crate and should use only public APIs.
- Use doc tests for public examples that should stay compilable.
- Test edge cases and error paths for new behavior.
- Add regression tests for bug fixes.
- Use temporary directories for filesystem tests. Do not touch the real home directory.
- Snapshot generated artifacts only when exact output is part of the behavior.
- Keep snapshots small and focused.
- Do not test behavior that the type system already guarantees.
- Prefer fake implementations behind traits over broad mocking libraries.
- Use table-driven tests for the same behavior across multiple inputs.
- Use async tests only when the project already has an async runtime requirement.

### Test Structure

- **Unit test**: place next to the implementation in the same file or module.
- **Integration test**: place under `tests/` when testing public API, CLI behavior, or crate-level wiring.
- **Helper function**: prefer local helper functions when setup is lightweight or argument-driven.
- **Shared fixture**: keep it in the test module first; move to `tests/common/` only when multiple integration test files need it.
- **Fake implementation**: prefer explicit fake types over global mocks.
- **Parameterized cases**: use a table of cases and loop over it when one behavior has many inputs.

Example unit test layout:

```rust
pub fn normalize_name(input: &str) -> String {
    input.trim().replace('_', "-")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn normalize_name_trims_whitespace_and_replaces_underscores() {
        assert_eq!(normalize_name(" rust_style "), "rust-style");
    }
}
```

## Syntax Rules

### Module Item Order

- Put module-level constants before types when they configure the following code.
- Prefer `type` aliases and enums before structs that use them.
- Put public structs, enums, and traits before their implementations.
- Put public functions before private helper functions.
- Keep `#[cfg(test)] mod tests` at the bottom of the file.
- Keep related types and their `impl` blocks close together.

### Structs

- Derive `Debug` for most domain structs.
- Derive `Clone`, `PartialEq`, `Eq`, `Ord`, or `Hash` only when the type actually needs that capability.
- Keep fields private when the type has invariants.
- Use constructors for types with validation or normalization.
- Use tuple structs for narrow newtypes and named-field structs when field names improve clarity.
- Avoid boolean constructor arguments; prefer an enum or options struct.

Value Object pattern:

```rust
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkillName(String);

impl SkillName {
    pub fn parse(value: impl Into) -> anyhow::Result {
        let value = value.into();
        if value.is_empty() {
            anyhow::bail!("skill name must not be empty");
        }
        Ok(Self(value))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}
```

### Enums

- Use enums for fixed states, providers, modes, and policy decisions.
- Convert user input strings into enums at the boundary.
- Avoid passing raw string modes through the codebase.
- Prefer exhaustive `match` statements for domain control flow.
- Implement `Display` when the enum has a stable user-facing representation.
- Implement `FromStr` or `TryFrom` when parsing user input.

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Provider {
    Claude,
    Codex,
}
```

### Functions and Methods

- Keep functions focused and small.
- Prefer borrowing in parameters: `&str`, `&Path`, `&[T]`.
- Return owned values when the function creates or transforms ownership.
- Use associated functions for pure construction.
- Keep I/O in module-level functions or dedicated service structs, not inside Value Object constructors.
- Prefer `Result` for expected failures.
- Prefer early returns for guard clauses.
- Avoid `async` unless the project has real concurrency or nonblocking I/O requirements.

### Getter and Conversion Naming

- Borrowed views use `as_*`: `as_str`, `as_path`, `as_slice`.
- Consuming conversions use `into_*`: `into_inner`, `into_path_buf`.
- Cheap copies can use the value name directly: `len`, `status`, `provider`.
- Avoid Java-style `get_*` unless following an existing local convention.
- Boolean queries use `is_*`, `has_*`, `can_*`, or `should_*`.
- Fallible parsing uses `parse`, `try_from`, or `from_str`.

### Ownership and Borrowing

- Accept `impl AsRef` only at outer convenience boundaries. Inside the codebase, pass `&Path`.
- Accept `impl Into` for constructors that store owned strings.
- Do not clone to satisfy the borrow checker until the ownership model is understood.
- Use `Cow` only when profiling or API shape shows it is worthwhile.
- Prefer slices over `&Vec` in function parameters.

### Collections

- Use `Vec` for ordered sequences.
- Use `BTreeMap` or sorted vectors when deterministic output order matters.
- Use `HashMap` when order is irrelevant and lookup dominates.
- Do not expose mutable collections directly from domain types.

### Strings and Formatting

- Use `&str` for borrowed string input and `String` for owned string storage.
- Use `format!` when constructing a new owned string from values.
- Use captured identifiers in formatting when it improves readability: `format!("{name}")`.
- Prefer `to_owned()` or `String::from` when converting a string literal to `String`.
- Avoid unnecessary `to_string()` in hot or repeated code.
- Keep user-facing text separate from machine-readable output.

### Numeric Conversions

- Use `usize` for indexing and collection lengths.
- Do not use `as` for narrowing integer conversions.
- Use `TryFrom` or `try_into` when a conversion can fail.
- Use newtypes when a number has a domain unit or invariant.
- Make lossy conversions explicit in the function or variable name.

## Error Handling

- Use `anyhow::Result` at application and CLI orchestration boundaries where errors are reported to humans and callers do not branch on error categories.
- Use concrete error types when callers or tests need to distinguish failure categories.
- `thiserror` is appropriate for deriving concrete error types. Do not introduce it just to wrap every possible failure.
- Add context at I/O, external command, parse, and config boundaries.
- A low-level error such as "No such file or directory" must include the operation and path.
- Use `?` for propagation.
- Do not use `unwrap` or `expect` in production code except for impossible states justified by a nearby invariant.

### Panic Policy

- Use `Result` for expected failures.
- Reserve `panic!` for logic bugs and impossible states.
- Avoid direct indexing when missing data is a normal possibility; use `get()` or explicit validation.
- Public APIs that can panic must document `# Panics`.
- Do not leave `todo!`, `unimplemented!`, or debugging `panic!` calls in production code.
- Use `unreachable!` only when the type system or previous validation makes the branch impossible.

Application-boundary error:

```rust
use anyhow::{Context, Result};

pub fn read_config(path: &Path) -> Result {
    std::fs::read_to_string(path)
        .with_context(|| format!("failed to read config file {}", path.display()))
}
```

Distinguishable domain error:

```rust
#[derive(Debug, thiserror::Error)]
pub enum ProviderError {
    #[error("unknown provider: {0}")]
    UnknownProvider(String),
}
```

## CLI Code

- Use typed structs and enums for CLI input.
- Keep argument parsing separate from execution.
- Do not perform filesystem or network work while parsing CLI arguments.
- Human-readable command results go to stdout.
- Progress, warnings, and diagnostics go to stderr.
- Machine-readable output must not be mixed with human logs.
- Keep exit codes coarse but meaningful enough for CI.

```rust
#[derive(Debug, clap::Parser)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Debug, clap::Subcommand)]
pub enum Command {
    Render(RenderArgs),
    Verify(VerifyArgs),
}
```

## Logging and Diagnostics

- Short-lived CLI tools may use `eprintln!` for warnings and progress.
- Libraries must not initialize global logging.
- Use `tracing` only when the project needs structured logs, spans, or long-running diagnostics.
- Keep human diagnostics on stderr and machine-readable command output on stdout.
- Include paths, command names, and config keys in diagnostics when they explain the failure.

## Filesystem Operations

- Use `Path` and `PathBuf` for paths. Do not build paths with string concatenation.
- Treat source tree traversal and symlink creation as separate behaviors.
- Do not follow symlinks while traversing source trees unless the behavior is explicitly required and tested.
- Use `DirEntry::file_type()` instead of `Path::is_dir()` when symlink behavior matters.
- Keep file-writing operations behind small functions that are easy to test with temporary directories.
- Distinguish files fully owned by the project from files also modified by users or external tools.
- Prefer writing to a temporary file and replacing the target when partial writes would be harmful.

Symlink-aware traversal without following symlinks:

```rust
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};

pub fn collect_regular_files(dir: &Path) -> Result> {
    let mut files = Vec::new();
    collect_regular_files_into(dir, &mut files)?;
    files.sort();
    Ok(files)
}

fn collect_regular_files_into(dir: &Path, files: &mut Vec) -> Result {
    for entry in std::fs::read_dir(dir)
        .with_context(|| format!("failed to read directory {}", dir.display()))?
    {
        let entry = entry?;
        let file_type = entry.file_type()?;
        let path = entry.path();

        if file_type.is_symlink() {
            continue;
        }
        if file_type.is_dir() {
            collect_regular_files_into(&path, files)?;
        } else if file_type.is_file() {
            files.push(path);
        }
    }
    Ok(())
}
```

## Config and Serialization

- Use typed structs for project-owned config.
- Use `serde(deny_unknown_fields)` where unknown input keys should be rejected.
- Do not reject unknown keys in external-tool-owned config files.
- Use `#[serde(rename_all = "...")]` to keep serialized field naming consistent.
- Use `#[serde(default)]` for optional input fields that have stable defaults.
- Use `#[serde(skip_serializing_if = "Option::is_none")]` when absent values should not appear in output.
- Use `#[serde(flatten)]` sparingly because it makes schemas less explicit.
- Separate fully generated files from user-owned files that receive managed updates.
- Do not overwrite user-owned or external-tool-owned config files wholesale.
- For managed config sync, update only the keys owned by the project and preserve unknown keys.
- Use stable ordering for generated output when review diffs or tests depend on order.
- Use a format-preserving edit strategy when comments, ordering, and unknown future keys must be preserved.

## Cargo Features

- Keep feature flags additive. Enabling a feature should not remove behavior.
- Avoid growing default features unless the behavior is truly the default user expectation.
- Keep optional dependency features named clearly after the capability they enable.
- Test important feature combinations explicitly.
- Consider `cargo test --all-features` when features affect compiled code paths.
- Do not use features as a runtime configuration substitute.

## Public API Design

- Reserve `pub` for APIs intended to be consumed outside the crate.
- Use `pub(crate)` for items shared across internal modules, including inside private modules when it clarifies that the item is not an external API.
- Keep items private when they are only used inside the current module.
- Keep public APIs smaller than internal APIs.
- Avoid exposing third-party types in public APIs unless that dependency is part of the intended API.
- Use `#[non_exhaustive]` for public enums or structs that may need new variants or fields.
- Seal traits that downstream crates should not implement.
- Treat new public trait implementations as semver-relevant changes.
- Do not expose mutable internal collections directly.

## Macro and Build Script Policy

- Prefer functions, traits, and generics before macros.
- Add a macro only when it removes repetition that ordinary Rust abstractions cannot express clearly.
- Avoid procedural macros unless the project has a strong reason for compile-time code generation.
- Keep test helper macros small and obvious.
- Do not add `build.rs` until native linking, environment probing, or code generation is actually required.
- Generated code should have tests or snapshots that make the generated output reviewable.

## Naming Conventions

- Modules and files: `snake_case`.
- Functions, methods, and variables: `snake_case`.
- Types, traits, and enum variants: `UpperCamelCase`.
- Constants and statics: `SCREAMING_SNAKE_CASE`.
- Generic type parameters: shor

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [furedea](https://github.com/furedea)
- **Source:** [furedea/agent-harness](https://github.com/furedea/agent-harness)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-furedea-agent-harness-rust-style
- Seller: https://agentstack.voostack.com/s/furedea
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
