Rust自定义错误管理库custom_error的使用,简化错误处理与提升代码可读性
Rust自定义错误管理库custom_error的使用,简化错误处理与提升代码可读性
这个crate包含一个宏,可以更容易地定义自定义错误,而无需编写大量样板代码。
custom_error!
宏会生成一个Rust枚举类型,包含所有可能的错误情况,并自动实现std::error::Error
和std::fmt::Display
trait。
基本用法
简单错误定义
extern crate custom_error;
use custom_error::custom_error;
custom_error!{MyError
Bad = "Something bad happened",
Terrible = "This is a very serious error!!!"
}
带参数的错误
extern crate custom_error;
use custom_error::custom_error;
custom_error!{SantaError
BadChild{name:String, foolishness:u8} = "{name} has been bad {foolishness} times this year",
TooFar = "The location you indicated is too far from the north pole",
InvalidReindeer{legs:u8} = "The reindeer has {legs} legs"
}
assert_eq!(
"Thomas has been bad 108 times this year",
SantaError::BadChild{name: "Thomas".into(), foolishness: 108}.to_string());
包装其他错误类型
#[macro_use] extern crate custom_error;
use std::{io, io::Read, fs::File, result::Result, num::ParseIntError};
custom_error! {FileParseError
Io{source: io::Error} = "unable to read from the file",
Format{source: ParseIntError} = "the file does not contain a valid integer",
TooLarge{value:u8} = "the number in the file ({value}) is too large"
}
fn parse_hex_file(filename: &str) -> Result<u8, FileParseError> {
let mut contents = String::new();
File::open(filename)?.read_to_string(&mut contents)?;
let value = u8::from_str_radix(&contents, 16)?;
if value > 42 {
Err(FileParseError::TooLarge { value })
} else {
Ok(value)
}
}
高级特性
可见性控制
custom_error!{pub MyError A="error a", B="error b"}
添加属性
custom_error!{#[derive(PartialEq,PartialOrd)] MyError A="error a", B="error b"}
assert!(MyError::A < MyError::B);
文档注释
custom_error!{
/// This is the documentation for my error type
pub MyError A="error a", B="error b"
}
高级错误消息
custom_error!{ pub MyError
Io{source: Error} = @{
match source.kind() {
NotFound => "The file does not exist",
TimedOut => "The operation timed out",
_ => "unknown error",
}
},
Unknown = "unknown error"
}
完整示例
#[macro_use]
extern crate custom_error;
use std::{fs::File, io::Read, num::ParseIntError};
// 定义一个自定义错误类型
custom_error! {AppError
ConfigNotFound{path: String} = "Configuration file not found at {path}",
ParseError{source: ParseIntError} = "Failed to parse input",
InvalidInput{value: i32} = "Invalid input value: {value}",
Unknown = "An unknown error occurred"
}
fn read_config(path: &str) -> Result<i32, AppError> {
let mut file = File::open(path).map_err(|_| AppError::ConfigNotFound { path: path.to_string() })?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let value = contents.trim().parse::<i32>().map_err(|e| AppError::ParseError { source: e })?;
if value < 0 {
Err(AppError::InvalidInput { value })
} else {
Ok(value)
}
}
fn main() {
match read_config("config.txt") {
Ok(value) => println!("Config value: {}", value),
Err(e) => match e {
AppError::ConfigNotFound { path } => eprintln!("Error: Could not find config at {}", path),
AppError::ParseError { source } => eprintln!("Parse error: {}", source),
AppError::InvalidInput { value } => eprintln!("Invalid input value: {}", value),
AppError::Unknown => eprintln!("An unknown error occurred"),
}
}
}
no_std支持
这个crate支持no-std
环境。要在你的项目中使用no-std版本,需要在Cargo.toml中禁用std特性:
[dependencies]
custom_error = { version = "1", default-features = false } # nostd compatible
1 回复
Rust自定义错误管理库custom_error的使用指南
介绍
custom_error
是一个简化Rust错误处理的库,它允许开发者通过简洁的语法定义自定义错误类型,同时自动实现std::error::Error
trait。这个库特别适合需要清晰错误分类和良好错误消息的项目。
主要特性
- 通过宏简化自定义错误定义
- 自动实现
Display
和Error
trait - 支持错误嵌套(错误链)
- 提供清晰的错误消息生成
安装
在Cargo.toml
中添加依赖:
[dependencies]
custom_error = "1.0"
基本使用方法
定义自定义错误
use custom_error::custom_error;
// 使用custom_error宏定义自定义错误类型
custom_error! {
pub MyError
InvalidInput { details: String } = "Invalid input: {details}",
NotFound { item: String } = "Item not found: {item}",
InternalError = "An internal error occurred",
}
使用自定义错误
// 使用自定义错误类型的函数示例
fn process_data(input: &str) -> Result<(), MyError> {
if input.is_empty() {
return Err(MyError::InvalidInput {
details: "Input cannot be empty".to_string(),
});
}
if input == "secret" {
return Err(MyError::NotFound {
item: "secret item".to_string(),
});
}
Ok(())
}
错误处理示例
fn main() {
// 处理不同错误情况的示例
match process_data("") {
Ok(_) => println!("Success!"),
Err(e) => match e {
MyError::InvalidInput { details } => {
eprintln!("Error: {}", details);
}
MyError::NotFound { item } => {
eprintln!("Error looking for: {}", item);
}
MyError::InternalError => {
eprintln!("Internal server error");
}
},
}
}
高级用法
错误嵌套
// 定义支持错误嵌套的数据库错误类型
custom_error! {
pub DatabaseError
ConnectionFailed { source: std::io::Error } = "Database connection failed",
QueryFailed { sql: String } = "Query failed: {sql}",
}
// 使用嵌套错误的函数示例
fn query_database() -> Result<(), DatabaseError> {
let _conn = std::net::TcpStream::connect("localhost:5432")
.map_err(|e| DatabaseError::ConnectionFailed { source: e })?;
// ...
Ok(())
}
动态错误消息
// 定义数学运算相关的错误类型
custom_error! {
pub MathError
DivisionByZero = "Division by zero",
NegativeLogarithm = "Logarithm of negative number",
}
// 除法运算函数,可能返回数学错误
fn divide(a: f64, b: f64) -> Result<f64, MathError> {
if b == 0.0 {
Err(MathError::DivisionByZero)
} else {
Ok(a / b)
}
}
fn main() {
// 处理数学错误的示例
match divide(10.0, 0.0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e), // 输出: "Error: Division by zero"
}
}
完整示例demo
下面是一个完整的示例程序,展示了custom_error库的完整使用流程:
use custom_error::custom_error;
// 1. 定义自定义错误类型
custom_error! {
#[derive(Debug)]
pub AppError {
// 文件读取错误,包含原始io错误
FileReadError { source: std::io::Error, path: String } = "Failed to read file at {path}",
// 解析错误,包含错误详情和行号
ParseError { details: String, line: u32 } = "Parse error at line {line}: {details}",
// 网络连接错误
NetworkError = "Network connection failed",
// 带状态码的HTTP错误
HttpError { status: u16 } = "HTTP request failed with status {status}",
}
}
// 2. 使用自定义错误的函数示例
fn read_and_parse_file(path: &str) -> Result<String, AppError> {
// 模拟文件读取操作
let content = std::fs::read_to_string(path)
.map_err(|e| AppError::FileReadError {
source: e,
path: path.to_string()
})?;
// 模拟解析过程
if content.is_empty() {
return Err(AppError::ParseError {
details: "Empty file content".to_string(),
line: 1
});
}
Ok(content)
}
// 3. 模拟网络请求函数
fn make_http_request() -> Result<(), AppError> {
// 模拟网络请求失败
Err(AppError::HttpError { status: 404 })
}
fn main() {
// 4. 处理文件读取和解析错误
match read_and_parse_file("nonexistent.txt") {
Ok(content) => println!("File content: {}", content),
Err(e) => match e {
AppError::FileReadError { path, .. } => {
eprintln!("Failed to read file: {}", path);
}
AppError::ParseError { details, line } => {
eprintln!("Parse error at line {}: {}", line, details);
}
_ => eprintln!("Unexpected error"),
},
}
// 5. 处理网络错误
match make_http_request() {
Ok(_) => println!("Request succeeded"),
Err(AppError::HttpError { status }) => {
eprintln!("HTTP error with status code: {}", status);
}
_ => eprintln!("Other error occurred"),
}
}
最佳实践
- 为不同的模块或功能定义独立的错误类型
- 使用有意义的错误变体名称
- 在错误中包含相关上下文信息
- 利用错误链来保留原始错误信息
与其他错误处理库的比较
custom_error
比thiserror
更轻量级,语法更简洁,适合不需要复杂错误处理的场景。对于需要更多自定义功能的大型项目,可以考虑thiserror
或snafu
。
通过使用custom_error
库,你可以显著减少错误处理相关的样板代码,同时保持错误信息的清晰和一致。