Claude
Skills
Sign in
Back

error-conversion-guide

Included with Lifetime
$97 forever

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.

Ads & Marketing

What this skill does


# 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_err` calls that could be automatic
- Repetitive error conversions

**Before**:
```rust
#[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**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
// 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**:
```rust
use thiserror::Error;

// Infrastructure layer
#[derive(Error, Debug)]
pub enum InfraError {
    #[error("Database error")]
    Database(#[from] sqlx::Error),

    #[error("HTTP error")]
    

Related in Ads & Marketing