先说最简单的Rust自定义错误。本文基于Rust1.59。
实现一个最基本的自定义错误只需要实现下面两个trait,这两个都是关于把错误信息输出的。
- 手动实现impl std::fmt::Debug的trait,一般直接添加注解即可:#[derive(Debug)]
- 手动实现impl std::fmt::Display的trait,,用于自定义输出错误文本信息。
Talk is cheap show the code:
use std::fmt;
#[derive(Debug)]
struct AppError {
kind: String,
message: String,
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind as &str {
"404" => write!(f,"程序出错:{{错误类型: {}, 错误原因: {}}}",self.kind, self.message),
_ => write!(f,"Sorry, something is wrong! Please Try Again!"),
}
}
}
fn produce_error() -> Result<(), AppError> {
Err(AppError {
kind: String::from("404"),
message: String::from("Page not found"),
})
}
fn main()
{
match produce_error(){
Err(err) => println!("{}",err),
_ => println!("No error"),
}
Ok(())
}
输出 :
解释:
先定义一个自定义错误类型的Struct,然后为这个Struct实现Display Trait,就是怎么输出的问题。
代码没什么好说的。但问题来了
问题1: println!("{}",err),但为什么不是{:?} 或 {:#?}那种(
pretty-print)把err结构打印出来?
解决方法1:导入Debug
#[derive(Debug)]
struct AppError {
kind: String,
message: String,
}
输出:
还是不满意?
解决方法2:把#[derive(Debug)]去掉,换自己的
impl fmt::Debug for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"AppError {{ 错误类型1: {}, 错误原因1: {} }}",
self.kind, self.message
)
}
}
输出:
完美。
解决问题1的完整代码如下
use std::fmt;
//#[derive(Debug)] //要使用默认Debug trait则把自定义的部分去掉
struct AppError {
kind: String,
message: String,
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind as &str {
"404" => write!(f,"程序出错:{{错误类型: {}, 错误原因: {}}}",self.kind, self.message),
_ => write!(f,"Sorry, something is wrong! Please Try Again!"),
}
}
}
impl fmt::Debug for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"AppError {{ 错误类型1: {}, 错误原因1: {} }}",
self.kind, self.message
)
}
}
fn produce_error() -> Result<(), AppError> {
Err(AppError {
kind: String::from("404"),
message: String::from("Page not found"),
})
}
fn main() {
match produce_error(){
Err(err) => println!("{:#?}",err),
_ => println!("No error"),
}
}
但又有问题了,
问题2:如果类似IO,Parse之类的系统错误怎么办?
先上代码:
use std::fs::File;
use std::io::{self, Read};
use std::{num, error};
use std::fmt;
#[derive(Debug)]
struct AppError {
kind: String,
message: String,
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind as &str {
"404" => write!(
f,
"程序出错:{{错误类型: {}, 错误原因: {}}}",
self.kind, self.message
),
_ => write!(f, "Sorry, something is wrong! Please Try Again!"),
}
}
}
//为AppError实现From::from<io::Error>的trait,这样AppError就能接住io::Error类型的error
impl From<io::Error> for AppError {
fn from(error: io::Error) -> Self {
AppError {
kind: String::from("io_error"),
message: error.to_string(),
}
}
}
//为AppError实现From::from<num::ParseIntError>的trait
impl From<num::ParseIntError> for AppError {
fn from(error: num::ParseIntError) -> Self {
AppError {
kind: String::from("parse_error"),
message: error.to_string(),
}
}
}
fn produce_error() -> Result<(), AppError> {
Err(AppError {
kind: String::from("404"),
message: String::from("Page not found"),
})
}
fn main() {
match produce_error(){
Err(err) => println!("{:#?}",err),
_ => println!("No error"),
}
let mut content = String::new();
match File::open("aaa.txt") {
Err(err) => {
let er:AppError= std::convert::From::from(err);//调用From<io::Error>
println!("{:#?}", er);
}
Ok(mut e) => {
match e.read_to_string(&mut content) {
Err(err) => println!("{:#?}", err),
Ok(_) => {
let number: usize = match content.parse() {
Ok(num) => {
println!("double num is:{}",num*2);
num
},
Err(err) => {
let er:AppError= std::convert::From::from(err);//调用From<num::ParseIntError>
println!("{:#?}", er);
0
}
};
},
};
}
};
}
为了让AppError"接住"io::Error和num::ParseIntError这样的系统错误,需要为AppError实现From<T>,那么当程序遇到io::Error时,通过match->Err(e)接住这个错误。所以定义了两个Trait
1、impl From<io::Error> for AppError
2、impl From<num::ParseIntError> for AppError
另外要实现上面match的操作输出自定义的AppError,就要让io::Error转换为AppError,否则就会输出默认的Error信息,那么就要 let er:AppError= std::convert::From::from(err)这样转换,这样就把AppError给“挂”上去了。
试一下:当把文件名改为abc.txt这个不存在的文件后,结果如下:
但存在2个问题
1、match的嵌套太销魂了。
2、error的返回每次都要转换,无法标准化。
下一篇介绍如何标准化自定义错误。