rust学习之错误处理

 组合器

在 Rust 中,组合器更多的是用于对返回结果的类型进行变换:例如使用 ok_or 将一个 Option 类型转换成 Result 类型,常见的组合器有:

or()和and():

  • or(),表达式按照顺序求值,若任何一个表达式的结果是 Some 或 Ok,则该值会立刻返回
  • and(),若两个表达式的结果都是 Some 或 Ok,则第二个表达式中的值被返回。若任何一个的结果是 None 或 Err ,则立刻返回。

Rust 还为我们提供了 xor(亦或操作) ,但是它只能应用在 Option 上 

or_else()和and_then()

跟 or() 和 and() 类似,唯一的区别在于,它们的第二个表达式是一个闭包。

map()与map_err():

map 可以将 Some 或 Ok 中的值映射为另一个

map_err可以将none或者Err中的中映射成另一个

map_or与map_or_else

map_or 在 map 的基础上提供了一个默认值.

map_or_else 与 map_or 类似,但是它是通过一个闭包来提供默认值:

ok_or与ok_or_else

他们可以将可以将 Option 类型转换为 Result 类型。其中 ok_or 接收一个默认的 Err 参数,而 ok_or_else 接收一个闭包作为 Err 参数。

filter

filter 用于对 Option 进行过滤。

fn main() {
    const ERR_DEFAULT: &str = "error message";
    let s1 = Some(3);
    let s2 = Some(6);
    let n = None;
    let fn_some = || Some(6);
    let fn_is_even = |x: &i8| x % 2 == 0;
    let s3 = Some("abc");
    let fn_character_count = |s: &str| s.chars().count();
    let o: Result<&str, &str> = Ok("abc");

    assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1
    assert_eq!(n.or(s1), s1);  // None or Some = Some
    assert_eq!(s1.filter(fn_is_even), n);  // Some(3) -> 3 is not even -> None
    assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6)
    assert_eq!(s1.or_else(fn_some), s1);  // Some1 or_else Some2 = Some1
    assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2
    assert_eq!(s3.map(fn_character_count), s1); // Some3 map = Some1
    assert_eq!(s3.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T)
    
    }

自定义错误类型

为了更好的定义错误,Rust 在标准库中提供了一些可复用的特征,如 std::error::Error 特征

use std::fmt::{Debug, Display};

pub trait Error: Debug + Display {
    fn source(&self) -> Option<&(Error + 'static)> { ... }
}

自定义错误类型只需要实现 Debug 和 Display 特征就可以作为 Err 来使用,source 方法是可选的,而 Debug 特征往往也无需手动实现,可以选择直接通过 derive 来派生。然而实现 Debug 和 Display 特征并不是作为 Err 使用的必要条件,实现这两个特征的目的是将错误打印输出。再者,实现 Debug 和 Display 特征也是为了能转换成 Error 的特征对象,而特征对象是在同一个地方使用不同类型的关键

use std::fmt;
// AppError 是自定义错误类型,它可以是当前包中定义的任何类型,
struct AppError {
    code: usize,
    message: String,
}

// 根据错误码显示不同的错误信息
// 为 AppError 实现 std::fmt::Display 特征
impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let err_msg = match self.code {
            404 => "Sorry, Can not find the Page!",
            _ => "Sorry, something is wrong! Please Try Again!",
        };

        write!(f, "{}", err_msg)
    }
}
// 为 AppError 实现 std::fmt::Debug 特征
impl fmt::Debug for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "AppError {{ code: {}, message: {} }}",
            self.code, self.message
        )
    }
}

fn produce_error() -> Result<(), AppError> {
    Err(AppError {
        code: 404,
        message: String::from("Page not found"),
    })
}

fn main() {
    match produce_error() {
        Err(e) => eprintln!("{}", e), // Sorry, Can not find the Page!
        _ => println!("No error"),
    }

    eprintln!("{:?}", produce_error()); // Err(AppError { code: 404, message: Page not found })

    eprintln!("{:#?}", produce_error());
    // Err(
    //     AppError { code: 404, message: Page not found }
    // )
}

例子中定义了一个错误类型,手动实现了 Display 特征后,该错误类型就可以作为 Err来使用了。同时还手动实现了 Debug 特征,这样我们就自定义了Debug 的输出内容,而不是使用派生后系统提供的默认输出形式。

错误类型的转换

Rust 为我们提供了 std::convert::From 特征,该特征允许我们将其它的错误类型转换成自定义的错误类型

pub trait From<T>: Sized {

        fn from(_: T) -> Self;

为自定义类型AppError实现 From 特征: 

use std::fs::File;
use std::io;

#[derive(Debug)]
struct AppError {
    kind: String,    // 错误类型
    message: String, // 错误信息
}

// 为 AppError 实现 std::convert::From 特征,由于 From 包含在 std::prelude 中,因此可以直接简化引入。
// 实现 From<io::Error> 意味着我们可以将 io::Error 错误转换成自定义的 AppError 错误
impl From<io::Error> for AppError {
    fn from(error: io::Error) -> Self {
        AppError {
            kind: String::from("io"),
            message: error.to_string(),
        }
    }
}

fn main() -> Result<(), AppError> {
    let _file = File::open("nonexistent_file.txt")?;

    Ok(())
}

// --------------- 上述代码运行后输出 ---------------
Error: AppError { kind: "io", message: "No such file or directory (os error 2)" }

File::open 返回的是 std::io::Error, 我们并没有进行任何显式的转换,它就能自动变成 AppError ,这是因为 ? 将错误进行隐式的强制转换

归一化不同的错误类型

在一个函数中有时需要返回不同的错误,而一个函数的返回值只有一种错误类型,为了解决此问题,需要将不同的错误类型归一化为同一种错误类型。要实现这个目的有三种方式:

  • 使用特征对象 Box<dyn Error>
  • 自定义错误类型
  • 使用 thiserror

 Box<dyn Error>

使用该方法的前提是错误类型能转换成 Error 的特征对象:

use std::fs::read_to_string;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
  let html = render()?;
  println!("{}", html);
  Ok(())
}

fn render() -> Result<String, Box<dyn Error>> {
  let file = std::env::var("MARKDOWN")?;
  let source = read_to_string(file)?;
  Ok(source)
}

 自定义返回的错误类型

对于没有实现Error 特征的类型,就无法使用Box<dyn Error>, 此时我们就需要自定义一个要返回的的错误类型,他要实现Display+Debug+From特征,相对来说会比较麻烦,但非常灵活

use std::fs::read_to_string;

fn main() -> Result<(), MyError> {
  let html = render()?;
  println!("{}", html);
  Ok(())
}

fn render() -> Result<String, MyError> {
  let file = std::env::var("MARKDOWN")?;
  let source = read_to_string(file)?;
  Ok(source)
}

#[derive(Debug)]
enum MyError {
  EnvironmentVariableNotFound,
  IOError(std::io::Error),
}

impl From<std::env::VarError> for MyError {
  fn from(_: std::env::VarError) -> Self {
    Self::EnvironmentVariableNotFound
  }
}

impl From<std::io::Error> for MyError {  //只有为自定义错误类型实现 Error 特征后,才能转换成 
                                         //相应的特征对象。
  fn from(value: std::io::Error) -> Self {
    Self::IOError(value)
  }
}

impl std::error::Error for MyError {}

impl std::fmt::Display for MyError {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      MyError::EnvironmentVariableNotFound => write!(f, "Environment variable not found"),
      MyError::IOError(err) => write!(f, "IO Error: {}", err.to_string()),
    }
  }
}

 使用 thiserror

thiserror可以帮助我们简化自定义错误类型带来的代码冗长,只要简单写写注释,就可以实现错误处理了,代码结构上非常简洁

use std::fs::read_to_string;

fn main() -> Result<(), MyError> {
  let html = render()?;
  println!("{}", html);
  Ok(())
}

fn render() -> Result<String, MyError> {
  let file = std::env::var("MARKDOWN")?;
  let source = read_to_string(file)?;
  Ok(source)
}

#[derive(thiserror::Error, Debug)]
enum MyError {
  #[error("Environment variable not found")]
  EnvironmentVariableNotFound(#[from] std::env::VarError),
  #[error(transparent)]
  IOError(#[from] std::io::Error),
}

还有更简洁的是anyhow ,它和 thiserror 是同一个作者开发的,两者如何选用,就看开发者是否关注自定义错误消息,关注则使用 thiserror(常见业务代码),否则使用 anyhow(编写第三方库代码)。还有一个error-chain 也是简单好用的库

use std::fs::read_to_string;

use anyhow::Result;

fn main() -> Result<()> {
    let html = render()?;
    println!("{}", html);
    Ok(())
}

fn render() -> Result<String> {
    let file = std::env::var("MARKDOWN")?;
    let source = read_to_string(file)?;
    Ok(source)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值