![v2-5349f7c57ac3eab86c8899ab0f9d8851_1440w.jpg?source=172ae18b](http://img-02.proxy.5ce.com/view/image?&type=2&guid=8e8033b9-d62f-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-5349f7c57ac3eab86c8899ab0f9d8851_1440w.jpg?source=172ae18b)
序言
现实世界处处凶险,充满了未知和异常,错误处理是保持代码健壮性必不可少的环节,处理错误的方式各有千秋,本文是对笔者在学习与实践过程中摸索得来的错误处理之道的梳理和总结.
为什么要进行错误处理
以如下Demo为例:
fn main() {
let path = "abc.txt";
println("{}",try_read_file(path));
}
fn try_read_file(path: &str) -> String {
std::fs::read_to_string(path).unwrap()
}
try_read_file
函数尝试对path指示的文件进行读取,但unwrap()
在该文件不存在时将导致程序崩溃,这显然不够健壮,在快速实现思路的阶段或者在出于演示目的的demo代码中可以使用unwrap()
快速打开Option
和Result
的包装,但是这个操作很危险,实际开发中应尽量避免这种暴力的方式.
如何在Rust中进行正确且优雅的错误处理
前导知识
1:实现"自定义错误类型"
阅读标准库中std::error::Error
的代码,剔除无关代码之后基本框架如下:
pub trait Error: Debug + Display {
// snip
fn source(&self) -> Option<&(dyn Error + 'static)> {
None }
}
如果采用自定义错误类型的方式来完成这项任务,我们需要为我们的自定义错误类型完成以下任务:
impl std::fmt::Display
Trait,并实现fmt
方法- 一般情况下可通过
#[derive(Debug)]
实现std::fmt::Debug
Trait - 实现
std::error::Error
的Trait,并根据error的级别决定是否覆盖source()
方法.如果当前错误类型是低级别错误,没有子错误,则返回None
,所以此时可以不覆盖soucre()
; 如果当前错误有子错误,则需要覆盖该方法
动手实现一个Demo如下:
use std::error::Error;
// 自定义Error,通过#[derive(Debug)]注解实现std::fmt::Debug的trait
#[derive(Debug)]
struct CustomError {
err: ChildError,
}
// 实现Display的trait
impl std::fmt::Display for CustomError {
// 一般情况下是固定写法
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "父类型错误~")
}
}
// 实现std::error::Error Trait,因为有子Error:ChildError,覆盖source()方法,返回Some(err)
impl std::error::Error for CustomError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.err