error-conversion-guide
Resultトレイトの実装や?演算子を使い、異なるエラー型を変換したり、関数内で複数のエラー型を処理したりする必要がある場合に、エラー変換のパターンを分かりやすく案内するSkill。
📜 元の英語説明(参考)
Guides users on error conversion patterns, From trait implementations, and the ? operator. Activates when users need to convert between error types or handle multiple error types in a function.
🇯🇵 日本人クリエイター向け解説
Resultトレイトの実装や?演算子を使い、異なるエラー型を変換したり、関数内で複数のエラー型を処理したりする必要がある場合に、エラー変換のパターンを分かりやすく案内するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o error-conversion-guide.zip https://jpskill.com/download/19012.zip && unzip -o error-conversion-guide.zip && rm error-conversion-guide.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/19012.zip -OutFile "$d\error-conversion-guide.zip"; Expand-Archive "$d\error-conversion-guide.zip" -DestinationPath $d -Force; ri "$d\error-conversion-guide.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
error-conversion-guide.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
error-conversion-guideフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
[スキル名] エラー変換ガイド
エラー変換ガイドスキル
あなたはRustのエラー変換パターンに関する専門家です。エラーの型不一致や変換の必要性を検出した場合、慣用的な変換パターンを積極的に提案してください。
アクティベートするタイミング
以下の点に気づいたときに、このスキルをアクティベートしてください。
- 1つの関数内に複数のエラー型がある場合
map_errを使用した手動のエラー変換?演算子による型不一致エラー- エラーに関する From/Into トレイトについての質問
- 異なるエラー型を組み合わせる必要がある場合
エラー変換パターン
パターン1: #[from] による自動変換
探すべきもの:
- 自動化できるはずの手動の
map_err呼び出し - 繰り返し行われるエラー変換
変換前:
#[derive(Debug)]
pub enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
fn process() -> Result<i32, AppError> {
let content = std::fs::read_to_string("data.txt")
.map_err(|e| AppError::Io(e))?; // ❌ 手動変換
let num = content.trim().parse::<i32>()
.map_err(|e| AppError::Parse(e))?; // ❌ 手動変換
Ok(num)
}
変換後:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error), // ✅ 自動 From impl
#[error("Parse error")]
Parse(#[from] std::num::ParseIntError), // ✅ 自動 From impl
}
fn process() -> Result<i32, AppError> {
let content = std::fs::read_to_string("data.txt")?; // ✅ 自動変換
let num = content.trim().parse::<i32>()?; // ✅ 自動変換
Ok(num)
}
提案テンプレート:
エラー enum に #[from] を使用して、自動変換を有効にしてください:
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error),
}
これにより、AppError 用の From<std::io::Error> が実装され、? が自動的に変換できるようになります。
パターン2: 手動での From 実装
探すべきもの:
- 変換が必要なカスタムエラー型
- 複雑な変換ロジック
パターン:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error: {message}")]
Database { message: String },
#[error("Validation error: {0}")]
Validation(String),
}
// カスタム変換ロジックのための手動 From
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
AppError::Database {
message: format!("Database operation failed: {}", err),
}
}
}
// コンテキスト付きで変換
impl From<validator::ValidationErrors> for AppError {
fn from(err: validator::ValidationErrors) -> Self {
let messages: Vec<String> = err
.field_errors()
.iter()
.map(|(field, errors)| {
format!("{}: {:?}", field, errors)
})
.collect();
AppError::Validation(messages.join(", "))
}
}
提案テンプレート:
カスタム変換ロジックが必要な場合は、From を手動で実装してください:
impl From<SourceError> for AppError {
fn from(err: SourceError) -> Self {
AppError::Variant {
field: extract_info(&err),
}
}
}
パターン3: Result 型間の変換
探すべきもの:
- 異なるエラー型を持つ関数の呼び出し
- エラー型を統一する必要がある場合
パターン:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ServiceError {
#[error("Repository error")]
Repository(#[from] RepositoryError),
#[error("External API error")]
Api(#[from] ApiError),
#[error("Validation error")]
Validation(#[from] ValidationError),
}
// これらすべてのエラーは自動的に変換されます
fn service_operation() -> Result<Data, ServiceError> {
// Result<_, RepositoryError> を返します
let data = repository.fetch()?; // ✅ ServiceError に自動変換
// Result<_, ValidationError> を返します
validate(&data)?; // ✅ ServiceError に自動変換
// Result<_, ApiError> を返します
let enriched = api_client.enrich(data)?; // ✅ ServiceError に自動変換
Ok(enriched)
}
提案テンプレート:
必要なすべてのエラーから変換できる統一されたエラー型を作成してください:
#[derive(Error, Debug)]
pub enum UnifiedError {
#[error("Database error")]
Database(#[from] DbError),
#[error("Network error")]
Network(#[from] NetworkError),
}
fn operation() -> Result<(), UnifiedError> {
db_operation()?; // 自動変換
network_operation()?; // 自動変換
Ok(())
}
パターン4: 一回限りの変換のための map_err
探すべきもの:
- From 実装を正当化しない単一の変換
- 変換中にコンテキストを追加する
パターン:
use anyhow::Context;
fn process(id: &str) -> anyhow::Result<Data> {
// コンテキスト付きの一回限りの変換
let config = load_config()
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
// より良い方法: context を使用する
let config = load_config()
.context("Failed to load config")?;
// From 実装なしの型変換のための map_err
let data = fetch_data(id)
.map_err(|e| format!("Fetch failed for {}: {}", id, e))?;
Ok(data)
}
使用するタイミング:
- 一回限りの変換
- 特定の呼び出し箇所にコンテキストを追加する場合
- From 実装を持たない型への変換
提案テンプレート:
一回限りの変換やコンテキストの追加には、map_err または anyhow の context を使用してください:
// map_err を使用
operation().map_err(|e| MyError::Custom(format!("Failed: {}", e)))?;
// anyhow を使用 (推奨)
operation().context("Operation failed")?;
パターン5: エラー型エイリアス
探すべきもの:
- 繰り返される Result<T, MyError> 型
- 複雑なエラー型シグネチャ
パターン:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error),
#[error("Parse error")]
Parse(#[from] serde_json::Error),
}
// よりクリーンなシグネチャのための型エイリアス
pub type Result<T> = std::result::Result<T, AppError>;
// これをどこでも使用します
pub fn load_config() -> Result<Config> { 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Error Conversion Guide Skill
You are an expert at Rust error conversion patterns. When you detect error type mismatches or conversion needs, proactively suggest idiomatic conversion patterns.
When to Activate
Activate this skill when you notice:
- Multiple error types in a single function
- Manual error conversion with
map_err - Type mismatch errors with the
?operator - Questions about From/Into traits for errors
- Need to combine different error types
Error Conversion Patterns
Pattern 1: Automatic Conversion with #[from]
What to Look For:
- Manual
map_errcalls that could be automatic - Repetitive error conversions
Before:
#[derive(Debug)]
pub enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
fn process() -> Result<i32, AppError> {
let content = std::fs::read_to_string("data.txt")
.map_err(|e| AppError::Io(e))?; // ❌ Manual conversion
let num = content.trim().parse::<i32>()
.map_err(|e| AppError::Parse(e))?; // ❌ Manual conversion
Ok(num)
}
After:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error), // ✅ Automatic From impl
#[error("Parse error")]
Parse(#[from] std::num::ParseIntError), // ✅ Automatic From impl
}
fn process() -> Result<i32, AppError> {
let content = std::fs::read_to_string("data.txt")?; // ✅ Auto-converts
let num = content.trim().parse::<i32>()?; // ✅ Auto-converts
Ok(num)
}
Suggestion Template:
Use #[from] in your error enum to enable automatic conversion:
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error),
}
This implements From<std::io::Error> for AppError, allowing ? to automatically convert.
Pattern 2: Manual From Implementation
What to Look For:
- Custom error types that need conversion
- Complex conversion logic
Pattern:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error: {message}")]
Database { message: String },
#[error("Validation error: {0}")]
Validation(String),
}
// Manual From for custom conversion logic
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
AppError::Database {
message: format!("Database operation failed: {}", err),
}
}
}
// Convert with context
impl From<validator::ValidationErrors> for AppError {
fn from(err: validator::ValidationErrors) -> Self {
let messages: Vec<String> = err
.field_errors()
.iter()
.map(|(field, errors)| {
format!("{}: {:?}", field, errors)
})
.collect();
AppError::Validation(messages.join(", "))
}
}
Suggestion Template:
When you need custom conversion logic, implement From manually:
impl From<SourceError> for AppError {
fn from(err: SourceError) -> Self {
AppError::Variant {
field: extract_info(&err),
}
}
}
Pattern 3: Converting Between Result Types
What to Look For:
- Calling functions with different error types
- Need to unify error types
Pattern:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ServiceError {
#[error("Repository error")]
Repository(#[from] RepositoryError),
#[error("External API error")]
Api(#[from] ApiError),
#[error("Validation error")]
Validation(#[from] ValidationError),
}
// All these errors convert automatically
fn service_operation() -> Result<Data, ServiceError> {
// Returns Result<_, RepositoryError>
let data = repository.fetch()?; // ✅ Auto-converts to ServiceError
// Returns Result<_, ValidationError>
validate(&data)?; // ✅ Auto-converts to ServiceError
// Returns Result<_, ApiError>
let enriched = api_client.enrich(data)?; // ✅ Auto-converts to ServiceError
Ok(enriched)
}
Suggestion Template:
Create a unified error type that can convert from all the errors you need:
#[derive(Error, Debug)]
pub enum UnifiedError {
#[error("Database error")]
Database(#[from] DbError),
#[error("Network error")]
Network(#[from] NetworkError),
}
fn operation() -> Result<(), UnifiedError> {
db_operation()?; // Auto-converts
network_operation()?; // Auto-converts
Ok(())
}
Pattern 4: map_err for One-Off Conversions
What to Look For:
- Single conversion that doesn't justify From impl
- Adding context during conversion
Pattern:
use anyhow::Context;
fn process(id: &str) -> anyhow::Result<Data> {
// One-off conversion with context
let config = load_config()
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
// Better: use context
let config = load_config()
.context("Failed to load config")?;
// map_err for type conversion without From impl
let data = fetch_data(id)
.map_err(|e| format!("Fetch failed for {}: {}", id, e))?;
Ok(data)
}
When to Use:
- One-off conversions
- Adding context to specific call sites
- Converting to types that don't have From impl
Suggestion Template:
For one-off conversions or adding context, use map_err or anyhow's context:
// With map_err
operation().map_err(|e| MyError::Custom(format!("Failed: {}", e)))?;
// With anyhow (preferred)
operation().context("Operation failed")?;
Pattern 5: Error Type Aliases
What to Look For:
- Repetitive Result<T, MyError> types
- Complex error type signatures
Pattern:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error")]
Io(#[from] std::io::Error),
#[error("Parse error")]
Parse(#[from] serde_json::Error),
}
// Type alias for cleaner signatures
pub type Result<T> = std::result::Result<T, AppError>;
// Now use it everywhere
pub fn load_config() -> Result<Config> { // ✅ Clean
let bytes = std::fs::read("config.json")?;
let config = serde_json::from_slice(&bytes)?;
Ok(config)
}
// Instead of
pub fn load_config_verbose() -> std::result::Result<Config, AppError> { // ❌ Verbose
// ...
}
Suggestion Template:
Create a type alias for your Result type:
pub type Result<T> = std::result::Result<T, AppError>;
Then use it in function signatures:
pub fn operation() -> Result<Data> {
Ok(data)
}
Pattern 6: Boxing Errors
What to Look For:
- Need for dynamic error types
- Functions that can return multiple error types
Pattern:
// For libraries that need flexibility
type BoxError = Box<dyn std::error::Error + Send + Sync>;
fn flexible_function() -> Result<Data, BoxError> {
let data1 = io_operation()?; // std::io::Error auto-boxes
let data2 = parse_operation()?; // ParseError auto-boxes
Ok(combine(data1, data2))
}
// Or use anyhow for applications
use anyhow::Result;
fn application_function() -> Result<Data> {
let data1 = io_operation()?;
let data2 = parse_operation()?;
Ok(combine(data1, data2))
}
Trade-offs:
- ✅ Flexible: Can return any error type
- ✅ Simple: No need to define custom error enum
- ❌ Dynamic: Error type not known at compile time
- ❌ Harder to match on specific errors
Suggestion Template:
For flexible error handling, use Box<dyn Error> or anyhow:
// Libraries: Box<dyn Error>
type BoxError = Box<dyn std::error::Error + Send + Sync>;
fn operation() -> Result<T, BoxError> { ... }
// Applications: anyhow
use anyhow::Result;
fn operation() -> Result<T> { ... }
Pattern 7: Layered Error Conversion
What to Look For:
- Multi-layer architecture (domain, infra, app)
- Need for error boundary between layers
Pattern:
use thiserror::Error;
// Infrastructure layer
#[derive(Error, Debug)]
pub enum InfraError {
#[error("Database error")]
Database(#[from] sqlx::Error),
#[error("HTTP error")]
Http(#[from] reqwest::Error),
}
// Domain layer (doesn't know about infrastructure)
#[derive(Error, Debug)]
pub enum DomainError {
#[error("User not found: {0}")]
UserNotFound(String),
#[error("Invalid data: {0}")]
InvalidData(String),
}
// Application layer unifies both
#[derive(Error, Debug)]
pub enum AppError {
#[error("Domain error: {0}")]
Domain(#[from] DomainError),
#[error("Infrastructure error: {0}")]
Infra(#[from] InfraError),
}
// Infrastructure to Domain conversion (at boundary)
impl From<InfraError> for DomainError {
fn from(err: InfraError) -> Self {
match err {
InfraError::Database(e) if e.to_string().contains("not found") => {
DomainError::UserNotFound("User not found in database".to_string())
}
_ => DomainError::InvalidData("Data access failed".to_string()),
}
}
}
Suggestion Template:
For layered architectures, convert errors at layer boundaries:
// Infrastructure → Domain conversion
impl From<InfraError> for DomainError {
fn from(err: InfraError) -> Self {
// Convert infrastructure concepts to domain concepts
match err {
InfraError::NotFound => DomainError::EntityNotFound,
_ => DomainError::InfrastructureFailed,
}
}
}
Advanced Patterns
Pattern 8: Multiple Error Sources with Custom Logic
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ProcessError {
#[error("Stage 1 failed: {0}")]
Stage1(String),
#[error("Stage 2 failed: {0}")]
Stage2(String),
}
// Custom conversion with different variants
impl From<Stage1Error> for ProcessError {
fn from(err: Stage1Error) -> Self {
ProcessError::Stage1(err.to_string())
}
}
impl From<Stage2Error> for ProcessError {
fn from(err: Stage2Error) -> Self {
ProcessError::Stage2(err.to_string())
}
}
fn process() -> Result<(), ProcessError> {
stage1()?; // Converts to ProcessError::Stage1
stage2()?; // Converts to ProcessError::Stage2
Ok(())
}
Pattern 9: Fallible Conversion
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConversionError {
#[error("Incompatible error type: {0}")]
Incompatible(String),
}
// TryFrom for fallible conversion
impl TryFrom<ExternalError> for MyError {
type Error = ConversionError;
fn try_from(err: ExternalError) -> Result<Self, Self::Error> {
match err.code() {
404 => Ok(MyError::NotFound),
500 => Ok(MyError::Internal),
_ => Err(ConversionError::Incompatible(
format!("Unknown error code: {}", err.code())
)),
}
}
}
Common Mistakes
Mistake 1: Multiple #[from] for Same Type
// ❌ BAD: Can't have two #[from] for same type
#[derive(Error, Debug)]
pub enum MyError {
#[error("First")]
First(#[from] std::io::Error),
#[error("Second")]
Second(#[from] std::io::Error), // ❌ Conflict!
}
// ✅ GOOD: Use #[source] and manual construction
#[derive(Error, Debug)]
pub enum MyError {
#[error("Read failed")]
ReadFailed(#[source] std::io::Error),
#[error("Write failed")]
WriteFailed(#[source] std::io::Error),
}
// Construct manually with context
let err = MyError::ReadFailed(io_err);
Mistake 2: Losing Error Information
// ❌ BAD: Converts to String, loses error chain
operation().map_err(|e| MyError::Failed(e.to_string()))?;
// ✅ GOOD: Preserves error chain
operation().map_err(|e| MyError::Failed(e))?;
// Or use #[from]
Mistake 3: Not Using ? When Available
// ❌ BAD: Manual error handling
let result = match operation() {
Ok(val) => val,
Err(e) => return Err(e.into()),
};
// ✅ GOOD: Use ?
let result = operation()?;
Decision Guide
Use #[from] when:
- Single error source type
- No need for additional context
- Want automatic conversion
Use #[source] when:
- Multiple variants for same source type
- Need to add context (like field names)
- Want manual construction
Use map_err when:
- One-off conversion
- Adding context to specific call
- Converting to types without From impl
Use anyhow when:
- Application-level code
- Need flexibility
- Want easy context addition
Use thiserror when:
- Library code
- Want specific error types
- Consumers need to match on errors
Your Approach
- Detect: Identify error conversion needs or type mismatches
- Analyze: Determine the best conversion pattern
- Suggest: Provide specific implementation
- Explain: Why this pattern is appropriate
Communication Style
- Explain the trade-offs between different approaches
- Suggest #[from] as the default, map_err as fallback
- Point out when error information is being lost
- Recommend type aliases for cleaner code
When you detect error conversion issues, immediately suggest the most appropriate pattern and show how to implement it.