Rust 错误处理
在Rust中,错误可以分为以下两大类别,如下表所示。
序号 | 名称和描述 | 用法 |
---|---|---|
1 | 可恢复的 可以处理的错误 | 结果枚举 |
2 | 不可恢复的 无法处理的错误 | 宕机宏 |
可恢复的错误是一种可以纠正的错误。当程序遇到可恢复的错误时,可以重试失败的操作或指定替代的行动方案。可恢复的错误不会导致程序突然失败。一个示例是文件未找到的错误。
不可恢复的错误会导致程序突然失败。如果发生不可恢复的错误,程序无法恢复到正常状态。它无法重试失败的操作或撤销错误。一个示例是试图访问数组末尾之外的位置。
与其他编程语言不同,Rust没有异常。它对可恢复的错误返回一个枚举类型 Result<T, E>,而如果程序遇到不可恢复的错误,则调用 panic 宏。panic宏会导致程序突然退出。
panic宏和不可恢复的错误
panic!宏允许程序立即终止并向程序的调用者提供反馈。当程序达到不可恢复状态时应使用它。
fn main() {
panic!("Hello");
println!("End of main"); //unreachable statement
}
在上面的示例中,当程序遇到panic!宏时,它将立即终止。
输出
thread 'main' panicked at 'Hello', main.rs:3
示例:panic! macro
fn main() {
let a = [10,20,30];
a[10]; //invokes a panic since index 10 cannot be reached
}
输出如下:
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.
程序可以调用panic!宏,如果违反了业务规则,如下所示的示例
fn main() {
let no = 13;
//try with odd and even
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.
Result枚举和可恢复的错误
Result枚举- 可用于处理可恢复的错误。它有两个变体 – OK 和 Err 。 T 和 E 是泛型类型参数。 T 表示在OK变体的成功情况下将返回的值的类型,而 E 表示在Err变体的失败情况下将返回的错误类型。
enum Result<T,E> {
OK(T),
Err(E)
}
让我们通过一个示例来理解这个问题−
use std::fs::File;
fn main() {
let f = File::open("main.jpg");
//this file does not exist
println!("{:?}",f);
}
程序会返回 OK(文件) 如果文件已经存在,并且 Err(错误) 如果找不到文件。
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 doesn't exist
match f {
Ok(f)=> {
println!("file found {:?}",f);
},
Err(e)=> {
println!("file not found \n{:?}",e); //handled error
}
}
println!("end of main");
}
注意 - 尽管文件未找到,该程序打印出主要事件的结束。这意味着程序已经优雅地处理错误。
输出
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());
}
}
注意 − 因为主要函数可以优雅地处理错误,所以会打印出主函数语句的末尾。
输出
Error msg is NOT_AN_EVEN
end of main
unwrap()和expect()
标准库中包含了一些辅助方法,这两个枚举类型 – Result<T,E>和Option
序号 | 方法 | 签名和描述 |
---|---|---|
1 | unwrap | unwrap(self): T 期望self为Ok/Some,并返回其中包含的值。如果是 Err 或 None ,则会引发一个带有错误内容的panic。 |
2 | expect | **expect(self, msg: &str): T ** 与unwrap行为相似,但除了显示错误内容外,还会输出自定义消息并引发panic。 |
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()
程序在发生panic情况下可以返回自定义错误信息。如下示例所示:
use std::fs::File;
fn main(){
let f = File::open("pqr.txt").expect("File not able to open");
//file does not exist
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.