八、错误处理

120 阅读2分钟

八、错误处理

Rust 中,存在两种错误:

  • 不可恢复的错误,这类错误场景被称为 panic
  • 可恢复的错误,这类错误通常由 Result<T, E> 提供

1. 不可恢复的错误

  • Rust 中,panic 即为不可恢复的错误

  • 遇到 panic 时的退出方式

    • panic = 'abort'
      • 直接退出,不展开清理内存,让操作系统来清理
      • 优点:项目的最终二进制文件更小
    • panic = 'unwinding'
      • 展开并回溯栈,Rust 程序自己清理内存
  • 使用示例

    // 手动 panic 时,使用 panic! 宏即可
    // 来自官方教程的一个示例
    fn main() {
        panic!("crash and burn");
    }
    
    

2. 可恢复的错误

  • 可恢复的错误,通常是指可以在程序中被捕获的错误,这个错误通常由 Result<T, E> 来提供

  • 对于一个错误,可通过 ? 符来向外传递这个错误

    // 来自官方教程的一个示例
    use std::io;
    use std::io::Read;
    use std::fs::File;
    
    fn read_username_from_file() -> Result<String, io::Error> {
      let mut f = File::open("hello.txt")?; // 如果文件打开失败,会将这个错误向外传播
      let mut s = String::new();
      f.read_to_string(&mut s)?; // 如果文件读取失败,会将这个错误向外传播
      Ok(s)
    }
    
    // 再简化一点
    fn read_username_from_file2() -> Result<String, io::Error> {
      let mut s = String::new();
    
      File::open("hello.txt")?.read_to_string(&mut s)?;
    
      Ok(s)
    }
    
    // 再再简化一点
    fn read_username_from_file3() -> Result<String, io::Error> {
      fs::read_to_string("hello.txt")
    }
    
    
  • ? 运算符可被用于返回值类型为 Result 的函数

    • 注意:只能在返回 Result 或者其它实现了 std::ops::Try 类型的函数中使用 ? 运算符
      • 解决方式1:将函数返回值类型修改为 Result (前提是没有其它限制阻止这样做)
      • 解决方式2: 通过合适的方法使用 matchResult 来处理 Result<T, E>
  • 之前我们一直在写 main,且没有给它指定返回值类型,实际上 main 函数允许以下两类返回值类型:

    • (),即我们通常默认的写法
    • Result<T, E>,即可以返回一个可以携带错误的 Result

3. 关于错误的处理

  • 如果代码 panic 了,那么应用程序是不可能自动恢复的

  • 适合 panic 的场景

    • 示例、代码原型和测试用例中
    • 当 developer 比编译器知道得更多时
    • 某些处理结果有可能会导致有害状态时
  • 适合返回 Result<T, E> 的场景

    • 某些错误预期会出现时