有两种方式来聚合多种类型的Error来进行函数间的传播:一种是使用Box<dyn std::error::Error>
来用trait object来代表所有类型的错误;另一种是使用自定义的enum
来装所有的错误。
主要参考:细说Rust错误处理 和 Result 与可恢复的错误.
TL; DR
Box<dyn std::error::Error>
通常用在不对错误进行恢复的时候。(不容易检测到错误类型)- 自定义一个
enum
通常用在需要对错误进行恢复的时候。(容易检测类型)
Box<dyn Error>
方式来聚合Error
fn do_something() -> std::result::Result<(),Box<dyn std::error::Error>>{
let path = "./dat";
let v = std::fs::read_to_string(path)?;
let x = std::str::from_utf8(v.as_bytes())?;
let u = x.parse::<u32>()?;
println!("num:{:?}",u);
Ok(())
}
三者分别返回不同的错误类型,但是都可以用Box<dyn std::error::Error>
来装。
- 优点: 方便书写。
- 缺点: 在传递后该Result后,该trait object 对应的实际的错误类型难以确定,应该需要用“反射”(
Any
trait)才能够确定。而不能直接match。
利用 enum
来聚合Error
内容来自细说Rust错误处理
自定义一个error
需要实现如下几步:
- 手动实现impl
std::fmt::Display
的trait,并实现fmt(...)
方法。 - 手动实现impl
std::fmt::Debug
的trait
,一般直接添加注解即可:#[derive(Debug)]
- 手动实现impl
std::error::Error
的trait
,并根据自身error
级别是否覆盖std::error::Error
中的source()
方法。
下面的内容中:
CustomError
为我们实现的自定义ErrorCustomError
有三个子类型ErrorCustomError
分别实现了三个子类型ErrorFrom
的trait,将其类型包装为自定义Error的子类型
好了,有了自定义的CustomError
,那怎么使用呢? 我们看代码:
use std::io::Error as IoError;
use std::str::Utf8Error;
use std::num::ParseIntError;
use std::fmt::{Display, Formatter};
fn main() -> std::result::Result<(),CustomError>{
let path = "./dat";
let v = read_file(path)?;
let x = to_utf8(v.as_bytes())?;
let u = to_u32(x)?;
println!("num:{:?}",u);
Ok(())
}
///读取文件内容
fn read_file(path: &str) -> std::result::Result<String, std::io::Error> {
std::fs::read_to_string(path)
}
/// 转换为utf8内容
fn to_utf8(v: &[u8]) -> std::result::Result<&str, std::str::Utf8Error> {
std::str::from_utf8(v)
}
/// 转化为u32数字
fn to_u32(v: &str) -> std::result::Result<u32, std::num::ParseIntError> {
v.parse::<u32>()
}
#[derive(Debug)]
enum CustomError {
ParseIntError(std::num::ParseIntError),
Utf8Error(std::str::Utf8Error),
IoError(std::io::Error),
}
impl std::error::Error for CustomError{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
CustomError::IoError(ref e) => Some(e),
CustomError::Utf8Error(ref e) => Some(e),
CustomError::ParseIntError(ref e) => Some(e),
}
}
}
impl Display for CustomError{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self {
CustomError::IoError(ref e) => e.fmt(f),
CustomError::Utf8Error(ref e) => e.fmt(f),
CustomError::ParseIntError(ref e) => e.fmt(f),
}
}
}
impl From<ParseIntError> for CustomError {
fn from(s: std::num::ParseIntError) -> Self {
CustomError::ParseIntError(s)
}
}
impl From<IoError> for CustomError {
fn from(s: std::io::Error) -> Self {
CustomError::IoError(s)
}
}
impl From<Utf8Error> for CustomError {
fn from(s: std::str::Utf8Error) -> Self {
CustomError::Utf8Error(s)
}
}