# rust-best-practices

Adopt this skill: https://proskills.md/skills/other/morganlinton-beautifulrust-skills-rust-best-practices

Page: https://proskills.md/skills/other/morganlinton-beautifulrust-skills-rust-best-practices
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: skills/rust-best-practices/SKILL.md
size: 8000
upstream: https://github.com/morganlinton/beautifulrust/blob/main/skills/rust-best-practices/SKILL.md

````text
---
name: rust-best-practices
description: >-
  Write idiomatic, robust, maintainable Rust. Use this skill whenever you write, review,
  refactor, debug, or explain Rust code: new crates, modules, functions, structs, traits,
  error types, async code, unsafe or FFI code, Cargo.toml, tests, or rustdoc. Trigger on
  any mention of Rust, cargo, crates, .rs files, the borrow checker, lifetimes, traits,
  clippy, or rustfmt, even when the user does not ask for "best practices" or "idiomatic"
  code. Synthesized from the Google Rust Style Guide, the Rust API Guidelines,
  Effective Rust, Rust Design Patterns, and Microsoft's Pragmatic Rust Guidelines.
---

# Rust best practices

Distilled from five Rust style and design guides, with a digest of each in `references/`.
Where they disagree, this file says which to follow and why.

## 1. Mindset

- **Write Rust-shaped code.** Don't port C++, Java, C#, or Python constructs one-to-one.
  Model errors with `Result`, resources with ownership and `Drop`, polymorphism with
  traits and enums, and nullability with `Option`. Inheritance-flavored tricks (`Deref` to
  a "base" struct, `Manager`/`Factory` types, `throw_if_null` helpers) are smells.
- **Make invalid states unrepresentable.** Push invariants into types: enums instead of
  bool flags and "must be X if Y" comments, newtypes for units and IDs, fallible
  constructors that validate once. Then the compiler enforces what a comment only requests.
- **Application code is not library code.** Library code (anything with external users)
  follows the API-design rules below strictly. Application code should favor simplicity
  and directness: concrete types, `String` or `&str` rather than `impl Into<String>`
  parameters, `anyhow`-style errors, `Vec` return values, no speculative generality.
- **Let the tools do the work.** rustfmt owns formatting, clippy owns idiom checks, the
  compiler owns memory safety. Spend your attention on design.

## 2. Before you hand back code

Run these and fix everything they report:

```bash
cargo fmt
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo doc --no-deps   # libraries: catches broken doc links and missing docs
```

- Fix a lint rather than silencing it. When a lint is genuinely wrong for one item, use
  `#[expect(clippy::name, reason = "...")]` on the narrowest item. `expect` warns when
  the lint stops firing, so stale suppressions get cleaned up. Use `#[allow]` only in
  generated code or macro output.
- Never put `#![deny(warnings)]` in source: new toolchains add warnings and break the
  build. Enforce warnings-as-errors in CI instead, with `cargo clippy -- -D warnings`.
- Configure lints once in `Cargo.toml` (`[lints]` or `[workspace.lints]`), not with
  scattered crate attributes. If a project has none, suggest `lints/lints.toml` and
  `lints/clippy.toml` from https://github.com/morganlinton/BeautifulRust; the suppression
  lints in it need `rust-version` 1.81 or later.

## 3. Ownership and borrowing

**Prefer unique ownership.** Reach for `Rc`/`Arc` only when shared ownership is part of
the design, and for `RefCell`/`Mutex` only when interior mutability is. They are not
failures, but they move checking from compile time to run time, so use them on purpose.

**Don't clone to make the borrow checker stop complaining.** A clone that exists only to
end a borrow produces a silent copy whose later changes are lost. Try these first, in
roughly this order:

1. Extend a temporary with a named `let`, or shorten a borrow with a block.
2. Split a long expression into typed intermediates so the failing step is visible.
3. Move a field out of `&mut self` with `std::mem::take` (needs `Default`),
   `std::mem::replace`, or `Option::take` instead of cloning it.
4. Split a large struct into smaller ones so disjoint fields can be borrowed separately.
   The compiler tracks disjoint field borrows inside one function, but not across
   method calls.
5. Clone small, immutable data deliberately, and say so in a comment if it's not obvious.
6. Use indices into an owning `Vec`, or `Rc<RefCell<_>>` with `Weak` back-pointers, as
   a last resort. Avoid self-referential structs.

```rust
// Move out of a &mut enum without cloning.
fn a_to_b(e: &mut MyEnum) {
    if let MyEnum::A { name, x: 0 } = e {
        *e = MyEnum::B { name: std::mem::take(name) };
    }
}
```

**Accept the most general borrowed type.** `&str` not `&String`, `&[T]` not `&Vec<T>`,
`&T` not `&Box<T>`, `Option<&T>` not `&Option<T>`. Deref coercion then lets callers pass
owned values, borrows, or literals. Where you only read a path, string, or byte buffer,
`impl AsRef<Path>` / `impl AsRef<str>` / `impl AsRef<[u8]>` is fine on parameters (never
on struct fields; store `PathBuf` and `String`). Library code that only iterates should
take `impl IntoIterator<Item = T>`; application code may simply take `&[T]`.

**Take ownership only when you need it, and then take it by value.** If a function stores
or consumes its argument, take `T`, not `&T` plus an internal `.clone()`. If it only
reads, take `&T`; if it mutates in place, `&mut T`. Return multiple values as a tuple or
struct, not through `&mut` out-parameters (reusing a caller-owned buffer, as
`Read::read` does, is the exception).

**Prefer owned data in structs.** A struct holding `&'a T` forces a lifetime onto every
container. Owning a `String` or `Vec<u8>` is usually simpler and rarely the bottleneck.
When a type does carry a lifetime, give it a descriptive name (`'src`, `'conn`); `'_` and
`'a` are fine on simple functions.

**Freeze after setup.** Build with `let mut`, then rebind `let data = data;` so later
code cannot mutate it by accident.

**Keep `Rc`, `Arc`, `Box`, and `RefCell` out of public signatures.** Take `&T`,
`&mut T`, or `T` and let callers decide how to own things. A service that must be shared
implements a cheap `Clone` by wrapping its state in `Arc<Inner>` internally.

## 4. Types and API design

### Naming

| Item | Case | Notes |
|---|---|---|
| Types, traits, enum variants | `UpperCamelCase` | Acronyms are one word: `Uuid`, `HttpClient` |
| Functions, methods, modules, locals | `snake_case` | |
| Constants, statics | `SCREAMING_SNAKE_CASE` | |
| Type parameters | short, from the main bound | `T`, `R: Read`, `W: Write` |
| Lifetimes | short lowercase | descriptive when on a type |

- No `get_` prefix on getters: `fn len(&self)`, `fn name(&self) -> &str`,
  `fn name_mut(&mut self)`. Use bare `get` only when there is one obvious thing to get
  (`Cell::get`). Related: `set_foo(T)`, `take_foo()`, `with_foo(|f| ..)` for closure
  access, and unsafe `_unchecked` siblings for checked accessors.
- Conversion prefixes signal cost and ownership: `as_` (free, borrow to borrow), `to_`
  (expensive, borrow to owned or borrow to borrow), `into_` (consumes `self`). Put `mut`
  in type order: `as_mut_slice`, not `as_slice_mut`.
- Constructors: `new` (primary), `with_capacity` / `new_with_foo` (variants), `try_new`
  (fallible), `from_foo` (conversion; pair important ones with a `From` impl). I/O
  resources use domain verbs: `File::open`, `TcpStream::connect`.
- `iter` / `iter_mut` / `into_iter` return types named `Iter` / `IterMut` / `IntoIter`;
  `keys()` returns `Keys`.
- Error types read verb-object-error: `ParseBoolError`, not `BoolParseError`.
- Avoid weasel suffixes `Service`, `Manager`, `Factory`, `Helper`, `Util`. Name the thing
  it is (`Bookings`, `Dispatcher`, `FooBuilder`). Don't repeat the module name in the
  type (`foo::Id`, not `foo::FooId`). Avoid abbreviations an outsider wouldn't know.
- If you implement `len()`, implement `is_empty()`.
- Feature names are bare: `std`, `serde`, `alloc`, never `use-std` or `no-std`.

### Model the domain with types

- **Newtypes** for semantic distinctions: `struct Miles(f64)` and `struct Kilometers(f64)`
  cannot be mixed, where a type alias can. Add `#[repr(transparent)]` when layout matters,
  and derive or forward the traits you need (`derive_mo
````

## File: README.md
upstream: https://github.com/morganlinton/beautifulrust/blob/main/README.md

````text
<p align="center">
  <img src="assets/banner.png" alt="Pixel art banner: a crab on rocky tide pools at sunset, with waves, sea stacks, and a forested headland, under the title Beautiful Rust and the subtitle Idiomatic Rust, distilled">
</p>

# Beautiful Rust

Three standard [Agent Skills](https://agentskills.io) and a lints config that teach coding
agents to write, review, port, and enforce idiomatic Rust:

- **`rust-best-practices`** loads whenever an agent writes or changes Rust, and steers it
  toward idiomatic, robust, maintainable code.
- **`rust-review`** reviews existing Rust code, a file, a crate, or a diff, and writes a
  prioritized report where every finding cites the rule it breaks and shows the fix.
- **`rust-port`** ports C, C++, Python, Go, or JavaScript to idiomatic Rust with the same
  behavior, porting the original tests first so you can prove the two match.
- **`lints/`** is a drop-in clippy configuration that enforces the machine-checkable rules
  in CI.

They work with Claude Code, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, and any other
agent that reads the Agent Skills format. Every code example in the skills compiles on
stable Rust, and every lint in the config was checked against current clippy.

The guidance is distilled from five sources, with a condensed digest of each bundled in
the skill so the agent can consult the original reasoning when it needs to:

- [Google Rust Style Guide](https://google.github.io/styleguide/rustguide.html)
- [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) (official rust-lang checklist)
- [Effective Rust](https://www.lurklurk.org/effective-rust/) by David Drysdale
- [Rust Design Patterns](https://rust-unofficial.github.io/patterns/) (idioms, patterns, anti-patterns)
- [Microsoft Pragmatic Rust Guidelines](https://microsoft.github.io/rust-guidelines/)

Where the sources disagree, the skill picks a side and says why.

## What it changes

Asked for a small semver-parsing library, an agent without the skill produced working
code with public fields, no `# Errors` docs, and `unwrap()` in its doc examples. With the
skill, the same agent kept the fields private, documented failure modes, and wrote doc
examples users can copy safely. Condensed from those two runs:

```rust
// Without the skill
/// A semantic version. The fields are public for convenience.
pub struct Version {
    pub major: u64,
    pub minor: u64,
    pub patch: u64,
    pub pre: Vec<Identifier>,
    pub build: Vec<String>,
}

impl Version {
    /// Parses a version string.
    ///
    /// ```
    /// let v = Version::parse("1.2.3").unwrap();
    /// ```
    pub fn parse(s: &str) -> Result<Version, ParseError> { /* ... */ }
}
```

```rust
// With the skill
/// A semantic version. Equality, hashing, and ordering ignore build metadata.
pub struct Version {
    major: u64,
    minor: u64,
    patch: u64,
    pre: Vec<Identifier>,
    build: Vec<String>,
}

impl Version {
    /// Parses a version string according to SemVer 2.0.0.
    ///
    /// # Errors
    ///
    /// Returns [`ParseVersionError`] if the string is empty, a numeric part has a
    /// leading zero or overflows `u64`, or an identifier contains invalid characters.
    ///
    /// ```
    /// # use semver_lite::Version;
    /// # fn main() -> Result<(), semver_lite::ParseVersionError> {
    /// let v = Version::parse("1.2.3-beta.1")?;
    /// assert_eq!(v.major(), 1);
    /// # Ok(())
    /// # }
    /// ```
    pub fn parse(s: &str) -> Result<Self, ParseVersionError> { /* ... */ }

    /// Returns the major version number.
    pub fn major(&self) -> u64 { self.major }
}
```

Across three test tasks (a library, a CLI, and refactoring a messy module), the skill
passed 29 of 30 checks against 24 of 30 without it. See [Testing the skills](#testing-the-skills).

## Install

Each skill is a folder under `skills/`: `rust-best-practices/`, `rust-review/`, and
`rust-port/`. Every option below puts those folders where your agent looks for skills.
Install all three; the review and port skills lean on the best-practices skill for style.

### Any agent, one command

The [`skills`](https://github.com/vercel-labs/skills) installer supports most agents
(Node 22.20 or later):

```bash
npx skills add morganlinton/BeautifulRust
```

Add `-g` to install for your user instead of the current project, or `-a <agent>` to pick
an agent, such as `-a codex` or `-a cursor`.

### Claude Code

Install it as a plugin so updates arrive with one command:

```bash
claude plugin install beautiful-rust --marketplace morganlinton/BeautifulRust
```

Inside a session, the same thing is:

```
/plugin marketplace add morganlinton/BeautifulRust
/plugin install beautiful-rust@beautiful-rust
```

This installs all three skills. They appear as `/beautiful-rust:rust-best-practices`,
`/beautiful-rust:rust-review`, and `/beautiful-rust:rust-port`, though you rarely need to
call them, since they trigger on their own. To try it for one session without installing, clone the repo
and run `claude --plugin-dir ./BeautifulRust`.

### OpenAI Codex

Install it as a plugin:

```bash
codex plugin marketplace add morganlinton/BeautifulRust
codex plugin add beautiful-rust@beautiful-rust
```

This installs all three skills as `beautiful-rust:rust-best-practices`,
`beautiful-rust:rust-review`, and `beautiful-rust:rust-port`. The same package works in
the ChatGPT desktop app.

### GitHub Copilot

With GitHub CLI 2.90 or later:

```bash
gh skill install morganlinton/BeautifulRust rust-best-practices
gh skill install morganlinton/BeautifulRust rust-review
gh skill install morganlinton/BeautifulRust rust-port
```

This installs into the current project by default. Add `--scope user` to install for your
user, or `--agent <name>` to install for a different agent.

### Gemini CLI

```bash
gemini skills install https://github.com/morganlinton/BeautifulRust --path skills/rust-best-practices
gemini skills install https://github.com/morganlinton/BeautifulRust --path skills/rust-review
gemini skills install https://github.com/morganlinton/BeautifulRust --path skills/rust-port
```

### Cursor or anything else: copy the folder

Clone the repo and copy the three skill folders into your agent's skills directory.
Most agents now read the shared `.agents/skills/` location:

```bash
git clone https://github.com/morganlinton/BeautifulRust
mkdir -p ~/.agents/skills
cp -R BeautifulRust/skills/rust-* ~/.agents/skills/
```

| Agent | User directory | Project directory |
|---|---|---|
| Claude Code | `~/.claude/skills/` | `.claude/skills/` |
| OpenAI Codex | `~/.agents/skills/` | `.agents/skills/` |
| GitHub Copilot | `~/.copilot/skills/` or `~/.agents/skills/` | `.github/skills/` or `.agents/skills/` |
| Cursor | `~/.cursor/skills/` or `~/.agents/skills/` | `.cursor/skills/` or `.agents/skills/` |
| Gemini CLI | `~/.gemini/skills/` or `~/.agents/skills/` | `.gemini/skills/` or `.agents/skills/` |

Claude Code does not read `.agents/skills/`, so use its own directory or the plugin.
Restart the agent if the skill doesn't show up right away.

### claude.ai

claude.ai takes one zip per skill, with the skill folder as the zip root:

```bash
cd skills
zip -r ../rust-best-practices.zip rust-best-practices -x '*.DS_Store' -x '*/evals/*'
zip -r ../rust-review.zip rust-review -x '*.DS_Store' -x '*/evals/*'
zip -r ../rust-port.zip rust-port -x '*.DS_Store' -x '*/evals/*'
```

Upload each under **Customize > Skills**. Code execution must be enabled first.

## Reviewing existing code

Ask your agent to review a file, crate, or diff, for example "review src/parser.rs" or
"what would you change in this PR". The `rust-review` skill runs `cargo fmt`, `clippy`, and
the tests when a toolchain is available, then writes a report with three tiers: must fix,
should fix, and consider. Every finding gives a `file:line` location, the rule it breaks
with its source (an API Guidelines code such as `C-GETTER`, an Effective Rust item, a
Microsoft guideline, or a c
````
