Rust - 错误处理

在 Rust 中,错误可以分为两大类,如下表所示。

Sr.No 名称 & 描述 用法
1

Recoverable

可处理的错误

结果枚举
2

UnRecoverable

无法处理的错误

panic 宏

可恢复错误是可以纠正的错误。 程序可以在遇到可恢复错误时重试失败的操作或指定备用操作过程。 可恢复的错误不会导致程序突然失败。 可恢复错误的一个示例是File Not Found(找不到文件)错误。

不可恢复的错误会导致程序突然失败。 如果发生不可恢复的错误,程序将无法恢复到正常状态。 它无法重试失败的操作或撤消错误。 不可恢复错误的一个示例是尝试访问超出数组末尾的位置。

与其他编程语言不同,Rust 没有例外。 对于可恢复的错误,它返回一个枚举 Result<T, E>;如果程序遇到不可恢复的错误,它会调用 panic 宏。 panic 宏导致程序突然退出。

Panic 宏和 Unrecoverable Errors

panic! 宏允许程序立即终止并向程序的调用者提供反馈。 当程序达到不可恢复的状态时应该使用它。

fn main() {
   panic!("Hello");
   println!("End of main"); //unreachable statement
}

在上面的例子中,程序遇到panic!宏时会立即终止。

输出

thread 'main' panicked at 'Hello', main.rs:3

示例: panic! 宏

fn main() {
   let a = [10,20,30];
   a[10]; //由于无法到达索引 10,因此引发 panic
}

输出如下图 −

warning: this expression will panic at run-time
--> main.rs:4:4
  |
4 | a[10];
  | ^^^^^ index out of bounds: the len is 3 but the index is 10

$main
thread 'main' panicked at 'index out of bounds: the len 
is 3 but the index is 10', main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

程序可以引发紧急情况! 如果违反业务规则,则使用宏,如下例所示 −

fn main() {
   let no = 13; 
   // 尝试奇数和偶数
   if no%2 == 0 {
      println!("Thank you , number is even");
   } else {
      panic!("NOT_AN_EVEN"); 
   }
   println!("End of main");
}

如果分配给变量的值为奇数,上面的示例将返回错误。

输出

thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

结果枚举和可恢复错误

枚举结果 - <T,E> 可用于处理可恢复的错误。 它有两个变体 − OKErrTE 是泛型类型参数。T 表示 OK 变体中成功情况下将返回的值的类型,E 表示失败情况下将返回的错误类型 在 Err 变体中。

enum Result<T,E> {
   OK(T),
   Err(E)
}

让我们通过一个例子来理解这一点 −

use std::fs::File;
fn main() {
   let f = File::open("main.jpg"); 
   // 该文件不存在
   println!("{:?}",f);
}

如果文件已存在,则程序返回OK(File);如果未找到文件,则返回Err(Error)

Err(Error { repr: Os { code: 2, message: "No such file or directory" } })

现在让我们看看如何处理 Err 变体。

以下示例处理使用 match 语句打开文件时返回的错误

use std::fs::File;
fn main() {
   let f = File::open("main.jpg");   // main.jpg 不存在
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
         println!("file not found \n{:?}",e);   // 处理错误
      }
   }
   println!("end of main");
}

注意 − 尽管未找到文件,程序仍会打印 main 事件的 end。 这意味着程序已经妥善处理了错误。

输出

file not found
Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
end of main

示例

如果数字不是偶数,is_even 函数将返回错误。 main() 函数处理此错误。

fn main(){
   let result = is_even(13);
   match result {
      Ok(d)=>{
         println!("no is even {}",d);
      },
      Err(msg)=>{
         println!("Error msg is {}",msg);
      }
   }
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}

注意 − 由于 main 函数妥善处理错误,因此会打印 main 语句的 end

输出

Error msg is NOT_AN_EVEN
end of main

unwrap() 和 expect()

标准库包含一些辅助方法,它们都枚举 − Result<T,E> 和 Option<T> 实施。 您可以使用它们来简化您确实不希望事情失败的错误情况。 如果方法成功,则使用"unwrap"函数提取实际结果。

Sr.No 方法 函数 & 描述
1 unwrap

unwrap(self): T

期望 self 为 Ok/Some 并返回其中包含的值。 如果它是 ErrNone,则会引发 panic 并显示错误内容。

2 expect

expect(self, msg: &str): T

行为类似于 unwrap,只不过除了错误内容之外,它还会在 panic 之前输出一条自定义消息。

unwrap()

unwrap() 函数返回操作成功的实际结果。 如果操作失败,它会返回带有默认错误消息的恐慌。 该函数是 match 语句的简写。 如下例所示 −

fn main(){
   let result = is_even(10).unwrap();
   println!("result is {}",result);
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}
result is true
end of main

修改上述代码,将奇数传递给 is_even() 函数。

unwrap() 函数会出现紧急情况并返回默认错误消息,如下所示

thread 'main' panicked at 'called `Result::unwrap()` on 
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace

expect()

如果发生紧急情况,程序可以返回自定义错误消息。 这如以下示例所示 −

use std::fs::File;
fn main(){
   let f = File::open("pqr.txt").expect("File not able to open");
   // 文件不存在
   println!("end of main");
}

函数expect()与unwrap()类似。 唯一的区别是可以使用expect 显示自定义错误消息。

输出

thread 'main' panicked at 'File not able to open: Error { repr: Os 
{ code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.