Rust错误处理
Mattakushi4322026-10-26查看 1900复制 615
何时激活:Rust 错误处理、thiserror、无论如何、自定义错误、结果、问号运算符、错误传播
📘如何使用这个技能
- 1复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
- 2打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
- 3粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
- 4直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景
💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。
SKILL.md3867 chars
---
name: rust-error-handling
description: "When to activate: Rust error handling, thiserror, anyhow, custom errors, Result, question mark operator, error propagation"
origin: zakr
metadata:
namespace: zakr/devtools-pack
version: 1.0.0
category: engineering
language: rust
platforms: [claude-code, codex, gemini-cli, cursor, aider, windsurf]
---
# Rust Error Handling
## Custom Errors with thiserror
`thiserror` generates `Display` and `Error` impls from derive macros.
```toml
[dependencies]
thiserror = "2"
```
```rust
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.
```toml
[dependencies]
anyhow = "1"
```
```rust
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.
```rust
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)
```rust
// 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
```rust
// 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
```rust
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()` from `anyhow` to add what operation failed
- **Returning `String` as error type** — hard for callers to programmatically handle
- **Swallowing errors in closures** — log or propagate; never silently discard