rust-best-practices
Loads whenever an agent writes or changes Rust, and steers it toward idiomatic, robust, maintainable code.
Three standard Agent Skills and a lints config that teach coding agents to write, review, port, and enforce idiomatic Rust.
npx skills add morganlinton/BeautifulRust
What's inside
Install them together. Each skill loads on its own when your agent needs it, and the lints keep the rules enforced in CI.
rust-best-practicesLoads whenever an agent writes or changes Rust, and steers it toward idiomatic, robust, maintainable code.
rust-reviewReviews 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-portPorts 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.
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:
/// 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> { /* ... */ }
}
/// 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 }
}
rust-best-practices
29/30 checks
rust-review
11/12 checks
rust-port
22/22 checks
These are real model runs and are billed. In the last comparison the writing skill passed 29 of 30 assertions against 24 of 30 without it, at roughly a third more tokens per task. The review skill passed 11 of 12 against 7 of 12, and the porting skill 22 of 22 against 19 of 22.
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.
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.
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.
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 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
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/
| 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 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.
rust-best-practices
skills/rust-best-practices/SKILL.md is about 500 lines, organized so an agent can scan
it while coding:
-D warnings, test, doc; lint policy
dyn
Option vs Result vs panic, library vs application error types
format!, imports, drop guards
Send assertions, lock discipline, async fn, yield points
SAFETY: comments, soundness, bindgen
rust-review
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 clippy lint), and the smallest code change that fixes it. Each
report ends with what the code already does well.
examples/review-inventory.md is a real review the skill
produced for the deliberately messy module in the eval cases.
inventory.rs
13 findings
Must fix 5
Should fix 5
Consider 3
What's already good 4
rust-port
Ask your agent to port a module, for example "port parser.c to Rust" or "rewrite this Python service in Rust". The rust-port skill reads the original and its tests, ports the tests first, designs a Rust API instead of transliterating the original, and verifies with cargo test and clippy. Each port comes with a PORTING.md that maps the old API and tests to the new ones and lists every deliberate difference.
| In the original | In Rust |
|---|---|
NULL and None | Option |
| Error codes and exceptions | An error enum |
| Out-parameters | Return values |
| Flag ints and magic strings | Typed enums and options |
| Dict insertion order | An ordered collection instead of a HashMap that would silently reorder output |
lints/
The skills teach the rules; the lints keep them from slipping back in. Copy
lints/clippy.toml next to your Cargo.toml and paste the [lints]
block from lints/lints.toml into it, then run:
cargo clippy --all-targets --all-features -- -D warnings
It flags unwrap() outside tests, unsafe blocks without a // SAFETY: comment, #[allow]
without a reason, any bool function parameter, silent lossy as casts, missing # Errors
and # Panics docs, and leftover dbg! and todo!. Lints marked [lib] in the file are
for libraries, and applications can drop them.
Every lint in the table is proven to fire. lints/check/ is a small crate
with one deliberate violation per lint, and this script runs clippy on it from the repo
root:
bash scripts/check-lints.sh
It fails if a lint stops firing, if something unexpected fires, or if the check crate's
copy of the table drifts from lints/lints.toml. allow_attributes and
allow_attributes_without_reason need rust-version = "1.81" or later, because clippy
switches them off for crates that can't use #[expect].
unsafe_op_in_unsafe_fn warn
each unsafe op needs its own unsafe block
missing_debug_implementations warn
every public type implements Debug
missing_docs warn
lib document every public item
unreachable_pub warn
pub items that aren't actually exported
rust_2018_idioms warn
2018-edition idioms, e.g. Formatter<'_>
all warn
pedantic warn
unwrap_used warn
use ?, or expect("why this cannot fail")
expect_used allow
expect with a reason is the sanctioned form
panic_in_result_fn warn
a fn returning Result shouldn't also panic
document reachable panics under # Panics
document failures under # Errors
allow_attributes warn
prefer #[expect] over #[allow] (needs rust-version 1.81+)
every suppression says why (needs rust-version 1.81+)
// SAFETY: comment on every unsafe block
one unsafe operation per block
bool parameters: use an enum or two methods
many bool fields: use an enum or bitflags
clone_on_ref_ptr warn
write Rc::clone(&x) so cheap clones are visible
redundant_clone warn
a clone whose original is never used again
rc_buffer warn
Rc
string_add warn
build strings with format! or push_str, not +
wildcard_imports warn
no glob imports (use super::* in tests is allowed)
as_conversions warn
prefer From / TryFrom
cast_sign_loss warn
module_name_repetitions allow
must_use_candidate allow
module_inception allow
a leaf module named after its main item is fine
enum_glob_use allow
use Enum::* inside a function that matches on it
clippy.tomlmax-fn-params-bools = 0Flag any function with a bool parameter, not just functions with four or more. A bool argument is unreadable at the call site (report(true)) and can't grow a third mode; use a two-variant enum or two methods instead.
allow-unwrap-in-tests = trueallow-expect-in-tests = trueallow-print-in-tests = trueAllow unwrap, expect, and printing in tests, where a panic is the assertion.
Sources
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's new
This page rebuilds itself from the BeautifulRust repository, so releases and commits show up here on their own.
Every rule, checked
The first BeautifulRust release with outside contributions. Huge thanks to @mvanhorn, who sent both of this release's headline features.
Port your code to Rust
BeautifulRust now ports existing code to Rust, and the repo is ready for contributors.
Review your Rust, not just write it
BeautifulRust now reviews existing code, not just new code, and ships lints to keep the rules enforced.
The skill was tested against three realistic Rust tasks with and without it loaded, graded by independent agents on 30 concrete assertions, and tightened where the first round showed gaps. Result: 29 of 30 with the skill versus 24 of 30 without, up from 25 of 26 versus 23 of 26 before the changes.
npx skills add morganlinton/BeautifulRust