⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

rust-best-practices

@morganlinton⭐ 1.5k stars

>-

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐⭐⭐⭐⭐ 1.5k on GitHubGitHub ↗

Very popular

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

Beautiful Rust

Three standard Agent Skills 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:

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:

// 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> { /* ... */ }
}
// 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.

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 installer supports most agents (Node 22.20 or later):

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:

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:

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:

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

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:

git clone https://github.com/morganlinton/BeautifulRust
mkdir -p ~/.agents/skills
cp -R BeautifulRust/skills/rust-* ~/.agents/skills/
AgentUser directoryProject 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:

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

// HOW IT'S BUILT

// FILES

2 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • skills/rust-best-practices/SKILL.md7.8 KBupstream ↗
    ---
    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
  • README.mdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/morganlinton-beautifulrust-skills-rust-best-practices.md

// REPO STATS

1.5k stars