常见的 error handing

程序中会出现的两种错误:错误、异常

异常

  • 异常是开发者无法预料且超出自己能力范围的错误,例如:空指针
  • 出现异常说明程序代码本身的逻辑就是有问题的

    错误

  • 典型的比如:建立TCP连接失败

  • 开发者可以预料这种问题并且有能力去解决
  • 错误是正常程序逻辑的一部分

    Java:try-catch

  • 代表:Java, Python, JS…

  • 问题:

    • 没有区分上面两种错误,统一使用Exception来表示它们
    • 这使得程序的流程控制显得比较混乱

      Golang:区分可恢复/不可恢复错误

  • 遇到不可恢复错误,程序奔溃退出(early crash)

  • 可恢复错误则就像正常的函数返回值一样

    Rust:两种实现可恢复/不可恢复错误语法

  • Result<T, E>是一个泛型枚举

  • panic!是一个宏

不可恢复错误

最简单的创建方法:使用宏

  1. fn main() {
  2. panic!("this is a error"); // 使用宏创建一个不可恢复错误
  3. println!("unreachable statement!");
  4. }

同时还有一些常见宏可导致不可恢复错误

断言

  1. fn main() {
  2. assert!(1 == 2);
  3. assert_eq!(1, 2); // 等效
  4. }

未实现的代码

  1. fn add(a: u32, b: u32) -> u32 {
  2. unimplemented!()
  3. }
  4. fn main() {
  5. println!("{}", add(1, 2));
  6. }

不应该被访问的代码

  1. fn divide_by_three(x: u32) -> u32 {
  2. for i in 0.. {
  3. if 3 * i < i {
  4. // 溢出了
  5. panic!("u32 overflow");
  6. }
  7. if x < 3 * i {
  8. return i - 1;
  9. }
  10. }
  11. // 这里其实是一定无法走到的,但是这里不写就会报错
  12. // expected `u32` because of return type
  13. // 按照其他语言的做法,可以return 0; 但是Rust可以用unreachable宏
  14. unreachable!();
  15. }

可恢复的错误

  • Result 源码 ```rust

pub enum Result { /// Contains the success value

  1. #[lang = "Ok"]
  2. #[stable(feature = "rust1", since = "1.0.0")]
  3. Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
  4. /// Contains the error value
  5. #[lang = "Err"]
  6. #[stable(feature = "rust1", since = "1.0.0")]
  7. Err(#[stable(feature = "rust1", since = "1.0.0")] E),

}

  1. - Ok/Err
  2. ```rust
  3. fn main() {
  4. let a: Result<u32, &'static str> = Result::Ok(1);
  5. println!("{:?}", a); // Ok(1)
  6. let b: Result<u32, &'static str> = Result::Err("result error");
  7. println!("{:?}", b); // Err("result error")
  8. }
  • 标准库的一些方法返回的是一个Result
    image.png
    1. fn main() {
    2. let result = std::fs::read("tmp/test.text");
    3. match result {
    4. Ok(data) => println!("{:?}", std::str::from_utf8(&data).unwrap()), // 不想处理from_utf8的Result error,就使用unwrap
    5. Err(err) => println!("{:?}", err), // Os { code: 2, kind: NotFound, message: "No such file or directory" }
    6. }
    7. }

自定义错误与问号表达式

错误如何传递,有时候我们需要把错误返回给上一层

问号表达式

  • 许多时候(尤其编写库的时候),不仅仅希望获取错误,更希望错误可以在上下文进行传递
  • 问号表达式可以简便地传递错误

    • 当函数的错误与当前错误的类型相同时,使用?可以直接将错误传递到函数外并终止函数执行
      1. fn foo() -> Result<T, E> {
      2. let x = bar()?; // bar的错误类型需要和foo相同(E)
      3. ...
      4. }
  • ?的作用是将 Result 枚举的正常值取出,如果有错误就将错误返回出去 ```rust fn bar() -> Result<u32, &’static str> { Ok(0) }

fn foo() -> Result<i32, &’static str> { let a = bar()?; Ok(a as i32) }

fn main() { println!(“{:?}”, foo()); // Ok(0) }

  1. ```rust
  2. fn bar() -> Result<u32, &'static str> {
  3. Ok(0)
  4. }
  5. fn foo() -> Result<i32, &'static str> {
  6. // 不用?表达式,就得用 match bar() 处理 Ok/Err
  7. let a = bar()?;
  8. Ok(a as i32)
  9. }
  10. fn main() {
  11. println!("{:?}", foo()); // Ok(0)
  12. }

创建自定义错误

包装自己的错误类型

  1. #[derive(Debug)]
  2. pub enum Error {
  3. IO(std::io::ErrorKind),
  4. }
  5. impl From<std::io::Error> for Error {
  6. fn from(error: std::io::Error) -> Self {
  7. Error::IO(error.kind())
  8. }
  9. }
  10. fn do_read_file() -> Result<(), Error> {
  11. let data = std::fs::read("/temp/foo.txt")?;
  12. let data_str = std::str::from_utf8(&data).unwrap();
  13. println!("{:?}", data_str);
  14. Ok(())
  15. }
  16. fn main() -> Result<(), Error> {
  17. do_read_file()?;
  18. Ok(())
  19. } // Error: IO(NotFound)