36·错误处理进阶

自定义错误类型

自定义错误类型

学习目标

  1. 设计自定义错误枚举
  2. 实现 DisplayError trait
  3. 使用 thiserror 简化

核心概念

手动实现

use std::fmt;
use std::io;
use std::num::ParseIntError;

#[derive(Debug)]
enum AppError {
    Io(io::Error),
    Parse(ParseIntError),
    NotFound(String),
    Custom(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::Parse(e) => write!(f, "Parse error: {}", e),
            AppError::NotFound(name) => write!(f, "Not found: {}", name),
            AppError::Custom(msg) => write!(f, "Error: {}", msg),
        }
    }
}

impl std::error::Error for AppError {}

impl From<io::Error> for AppError {
    fn from(e: io::Error) -> Self { AppError::Io(e) }
}

impl From<ParseIntError> for AppError {
    fn from(e: ParseIntError) -> Self { AppError::Parse(e) }
}

fn read_number(path: &str) -> Result<i32, AppError> {
    let content = std::fs::read_to_string(path)?;  // io::Error → AppError
    let number = content.trim().parse()?;           // ParseIntError → AppError
    Ok(number)
}

使用 thiserror

[dependencies]
thiserror = "1"
use thiserror::Error;

#[derive(Error, Debug)]
enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Parse error: {0}")]
    Parse(#[from] std::num::ParseIntError),

    #[error("Not found: {0}")]
    NotFound(String),

    #[error("Custom error: {0}")]
    Custom(String),
}

使用 anyhow(应用层)

[dependencies]
anyhow = "1"
use anyhow::{Context, Result};

fn read_config(path: &str) -> Result<Config> {
    let content = std::fs::read_to_string(path)
        .context("Failed to read config file")?;

    let config: Config = serde_json::from_str(&content)
        .context("Failed to parse config")?;

    Ok(config)
}

thiserror vs anyhow

场景特点
thiserror库代码自定义错误类型,精确匹配
anyhow应用代码Box<dyn Error>,快速开发

实践练习

练习 1:验证错误

use thiserror::Error;

#[derive(Error, Debug)]
enum ValidationError {
    #[error("字段 '{field}' 不能为空")]
    EmptyField { field: String },

    #[error("字段 '{field}' 长度必须在 {min} 到 {max} 之间")]
    LengthOutOfRange { field: String, min: usize, max: usize },

    #[error("值 {value} 不在有效范围内")]
    OutOfRange { value: i32 },
}

fn validate_username(name: &str) -> Result<(), ValidationError> {
    if name.is_empty() {
        return Err(ValidationError::EmptyField {
            field: "username".to_string(),
        });
    }
    if name.len() < 3 || name.len() > 20 {
        return Err(ValidationError::LengthOutOfRange {
            field: "username".to_string(),
            min: 3,
            max: 20,
        });
    }
    Ok(())
}

fn main() {
    match validate_username("") {
        Ok(()) => println!("有效"),
        Err(e) => println!("验证失败: {}", e),
    }
}

小结

工具用途
thiserror库中定义错误类型
anyhow应用中快速错误处理
#[from]自动 From 转换
#[error("...")]Display 实现

练习编辑器

rust
Loading...