Rust error handling
A curated pack of custom Claude Code skills for developers — installable as a Claude Code plugin marketplace.
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill rust-error-handlingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
When to activate: Rust error handling, thiserror, anyhow, custom errors, Result, question mark operator, error propagation
SKILL.md
3.8 KB, as published. Nobody here has run it
Rust Error Handling
Custom Errors with thiserror
thiserror generates Display and Error impls from derive macros.
[dependencies]
thiserror = "2"
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("user {id} not found")]
UserNotFound { id: u64 },
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("validation failed on field '{field}': {message}")]
Validation { field: String, message: String },
#[error("external service '{service}' returned {status}")]
ExternalService { service: String, status: u16 },
#[error(transparent)]
Unexpected(#[from] anyhow::Error),
}
Application Errors with anyhow
anyhow is ideal for application code where you need rich context but don't care about the exact type.
[dependencies]
anyhow = "1"
use anyhow::{Context, Result, bail, ensure};
fn load_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("failed to read config from {path}"))?;
let config: Config = toml::from_str(&content)
.context("failed to parse config as TOML")?;
ensure!(config.port > 1024, "port must be > 1024, got {}", config.port);
if config.workers == 0 {
bail!("workers must be at least 1");
}
Ok(config)
}
The ? Operator
? returns early on error, applying From conversion if needed.
fn process() -> Result<Output, AppError> {
let data = read_file("input.txt")?;
let parsed = parse_json(&data)?;
let result = transform(parsed)?;
Ok(result)
}
// ? works with Option via .ok_or / .ok_or_else
fn find_setting(name: &str) -> Result<String, AppError> {
settings()
.get(name)
.cloned()
.ok_or_else(|| AppError::Validation {
field: name.to_string(),
message: "setting not found".into(),
})
}
Error Hierarchy (Library vs. Application)
// Library crates: use thiserror for typed, structured errors
#[derive(Debug, thiserror::Error)]
pub enum MyLibError {
#[error("invalid input: {0}")]
InvalidInput(String),
#[error(transparent)]
Io(#[from] std::io::Error),
}
// Application code: use anyhow for ergonomic context chains
fn run() -> anyhow::Result<()> {
my_lib::process("data.txt")
.context("processing step failed")?;
Ok(())
}
Converting Between Error Types
// map_err for targeted conversion
fn parse_id(s: &str) -> Result<u64, AppError> {
s.parse::<u64>().map_err(|e| AppError::Validation {
field: "id".into(),
message: e.to_string(),
})
}
Error Handling in Main
fn main() -> anyhow::Result<()> {
let config = load_config("config.toml")?;
run(config)?;
Ok(())
}
// With custom exit codes
fn main() {
if let Err(e) = run() {
eprintln!("Error: {e:#}"); // {:#} prints the full error chain
std::process::exit(1);
}
}
Common Anti-Patterns
- Using
Box<dyn Error>in library APIs — prevents callers from matching on specific errors unwrap()/expect()in production code — always propagate or handle errors explicitly- Losing error context with bare
?— use.context()fromanyhowto add what operation failed - Returning
Stringas error type — hard for callers to programmatically handle - Swallowing errors in closures — log or propagate; never silently discard