自定义错误类型
学习目标
- 设计自定义错误枚举
- 实现
Display 和 Error trait
- 使用
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)?;
let number = content.trim().parse()?;
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 实现 |