Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Rust Errors

ASecurity

Design Rust error types and panic policy — Result over unwrap, thiserror for libraries, anyhow for binaries, context that survives the call stack. Use when code calls unwrap or expect outside tests, when designing or refactoring an error enum, when choosing between thiserror and anyhow, or when the user asks how errors should be handled or propagated.

2 stars
0 votes
0 copies
1 views
Added 9/19/2026
developmentrustgobashexpressrefactoringapi

Works with

cliapi

Security Analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned 9/19/2026

$npx -y skills add rewrite-rs/skills --skill rust-errors --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rust Errors?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Rust Errors
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rewrite-rs-rust-errors/badge)](https://www.skillsdirectory.com/skills/rewrite-rs-rust-errors)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: rust-errors
description: Design Rust error types and panic policy — Result over unwrap, thiserror for libraries, anyhow for binaries, context that survives the call stack. Use when code calls unwrap or expect outside tests, when designing or refactoring an error enum, when choosing between thiserror and anyhow, or when the user asks how errors should be handled or propagated.
---

# Rust Errors

An error type is a contract about what can go wrong and what the caller does
about it: which failures panic, which become a `Result`, which shape the error
takes, how much context survives the call stack. Making the failure impossible
is `/type-driven-design`; whether shape changes break callers, `/rust-api-design`.

## The panic policy

`unwrap` and `expect` are assertions about invariants, not error handling.
Acceptable: in tests, in `main` for a startup precondition, and after a check
the compiler cannot see — where `expect` names the invariant and carries the
values. Unacceptable: in library code on any input-derived value, or anywhere
the justification is "this can't fail" without saying why.

Not every failure is a `Result`. A broken internal invariant with no
caller-recoverable path should panic — the program is already in a state the type
system promised it would not reach. Input validation is the opposite: whatever
the input, the caller can respond, so it is always a `Result`.

## `Result` all the way to a boundary

Errors propagate with `?` to the layer that can actually decide — retry, report,
exit. `?` converts through `From`, which is why a `#[from]` on an enum variant
removes a `map_err` closure at every call site.

## Which shape: the boundary decides

One question settles it: **does this error cross a public API boundary you will
maintain across releases?**

No — an internal crate, a closed set of failures, an error the caller only ever
prints — it is an enum, and the enum is the cheaper, better answer:

```rust,ignore
#[derive(Debug, thiserror::Error)]
enum LoadError {
    #[error("cannot read {path}")]
    Read { path: PathBuf, #[source] source: std::io::Error },
    #[error("malformed config in {path}")]
    Parse { path: PathBuf, #[source] source: toml::de::Error },
}
```

Yes — a published library whose callers upgrade without editing their code — it is
a struct wrapping a private kind, exposing the questions callers actually ask:

```rust,ignore
pub struct ConfigError { kind: ErrorKind }

impl ConfigError {
    pub fn is_not_found(&self) -> bool { /* match on the private kind */ }
    pub fn path(&self) -> &Path { /* ... */ }
}
```

The full struct pattern, compiling, is in `ERROR-TYPES.md`.

The public enum fails at that boundary in four independent ways: a variant like
`Parse(toml::de::Error)` promises for ever which TOML crate you use; adding a
failure mode breaks caller matches; `#[non_exhaustive]` does not save it, because
it forces every caller into a `_ =>` arm from day one and so removes the exhaustive
matching that was the reason to expose an enum; and it pushes work onto callers,
who re-derive "the config file is missing" from
`ConfigError::Io(e) if e.kind() == NotFound`. The precedent is `std::io::Error`,
which is exactly this pattern.

`thiserror` still does the mechanical work either way — `#[source]` chaining and
`Display` forwarding. Binaries use `anyhow`: a binary error is a message a human
reads once, so a boxed dynamic error with context is enough. A binary large enough
to have internal library crates uses `thiserror` inside them and `anyhow` only at
the top, and `anyhow` in a published library forces every downstream caller to give
up matching on concrete types — that is the actual reason for the split, not taste.

## Error taxonomy

Split the enum by what the caller does about it, not by where it was raised. If
two variants always get identical handling, they are one variant. If one variant
is retryable and another fatal, they must be distinguishable without
string-matching a message — an `is_retryable()` or `kind()` accessor on a coarse
enum. On a public struct error the growth question does not arise, because the
kind is private and adding to it is not a breaking change; `#[non_exhaustive]` is
for the enum that is public despite the guidance above, buying non-breaking
growth at the cost of exhaustive matching. The semver rules live in
`/rust-api-design`.

## Panics that are the right answer

Panic means stop the program, not "return an error loudly": a detected bug — a
violated internal invariant — panics rather than returning an `Error`, because
there is no caller who can do anything correct with it. `catch_unwind` is a
last resort, followed by a controlled restart of the unit that panicked, never
by carrying on as though nothing happened. For invariants too costly to check
in release builds, `debug_assert!` keeps the check in dev and test builds.

## Deferrals

Making the illegal state unrepresentable is `/type-driven-design`; whether a
shape change is a semver break is `/rust-api-design`; cancellation and timeouts
are `/async-rust`; expression-level cleanup is `/idiomatic-rust`.

## Verification

```bash
cargo clippy --all-targets   # add -- -D warnings only if the repo has no lint config
cargo test
```

Run clippy at the lint level configured in the repo — a `clippy.toml` or its own
lint attributes stay, and a stricter level never goes on top. Then the targeted
audit the agent runs itself and reads:

```bash
rg '\.unwrap\(\)|\.expect\(' --glob '!**/tests/**' --glob '!**/benches/**' src/
```

Every surviving hit needs a one-line justification. `clippy::unwrap_used` and
`clippy::expect_used` are `restriction` lints: propose them for the repo lint
config, do not switch them on unilaterally, or override a level already set.

Attribution

rewrite-rsrewrite-rs
View sourceSee grades on GitHubMore from rewrite-rs →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

285172 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10311 votes
View all in development →