文章目录
前言
Rust 有一套独特的处理异常情况的机制,它并不像其它语言中的 try 机制那样简单。
在Rust 中的错误分为两大类:可恢复错误和不可恢复错误。大多数编程语言用
Exception
(异常)类来表示错误。在 Rust 中没有 Exception。对于可恢复错误用
Result<T, E>
类来处理,对于不可恢复错误使用
panic!
宏来处理。
1、不可恢复错误
- 由编程中无法解决的逻辑错误导致的,例如访问数组末尾以外的位置。
1.1、panic! 宏的使用
宏的使用较为简单,让我们来看一个具体例子:
fnmain(){panic!("Error occured");println!("Hello, rust");}
运行结果:
很显然,程序并不能如约运行到
println!("Hello, rust")
,而是在
panic!
宏被调用时停止了运行,不可恢复的错误一定会导致程序受到致命的打击而终止运行。
1.2、通过 Powershell命令行分析错误原因
我们来分析一下终端命令行中的报错信息:
thread 'main' panicked at 'Error occured', src\main.rs:2:5
note:Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
- 第一行输出了 panic! 宏调用的位置以及其输出的错误信息
- 第二行是一句提示,翻译成中文就是"通过
RUST_BACKTRACE=full
环境变量运行以显示回溯"。接下来看一下回溯(backtrace
)信息:
stack backtrace:0:std::panicking::begin_panic_handler
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:5841:core::panicking::panic_fmt
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:1422:error_deal::main
at .\src\main.rs:23:core::ops::function::FnOnce::call_once<void(*)(),tuple$<>>
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\core\src\ops\function.rs:248
回溯是不可恢复错误的另一种处理方式,它会展开运行的栈并输出所有的信息,然后程序依然会退出。通过大量的输出信息,我们可以找到我们编写的 panic! 宏触发的错误。
2、可恢复的错误
- 如果访问一个文件失败,有可能是因为它正在被占用,是正常的,我们可以通过等待来解决。
2.1、Rustlt<T,E>枚举类的使用
此概念十分类似于 Java 编程语言中的异常,而在 C 语言中我们就常常将函数返回值设置成整数来表达函数遇到的错误,在 Rust 中通过
Result<T, E>
枚举类作返回值来进行异常表达:
enumResult<T,E>{Ok(T),Err(E),}//T的类型不定,相当于C++中模板的写法
我们知道
enum
常常与
match
配合使用,当匹配到
OK
时就会执行相应代码。
在 Rust 标准库中可能产生异常的函数的返回值都是
Result
类型。
例如:当我们尝试打开一个文件时:
usestd::fs::File;fnmain(){let fp =File::open("hello_rust.txt");match fp {Ok(file)=>{println!("File opened successfully.");},Err(err)=>{println!("Failed to open the file.");}}}//OK里的参数file是File类型,相当于填充了枚举里的T类型
如果
hello_rust.txt
文件不存在,会打印 Failed to open the file.
当然,我们在枚举类章节讲到的
if let
模式匹配语法可以简化
match
语法块:
usestd::fs::File;fnmain(){let fp =File::open("hello_rust.txt");ifletOk(file)= fp {println!("File opened successfully.");}else{println!("Failed to open the file.");}}
2.2、Result 类的unwrap() 和 expect(message: &str) 方法
- 将一个可恢复错误按不可恢复错误处理
举个例子:
usestd::fs::File;fnmain(){let fp1 =File::open("hello_rust.txt").unwrap();let fp2 =File::open("hello_rust.txt").expect("Failed to open.");}
- 这段程序相当于在 Result 为
Err
时调用 panic!宏 - 两者的区别在于
expect
能够向 panic! 宏发送一段指定的错误信息 panic!
宏是不可恢复错误,这样就完成了转变
3、可恢复的错误的传递
之前所讲的是接收到错误的处理方式,接下来讲讲怎么把错误信息传递出去
我们先来编写一个函数:
fnf(i:i32)->Result<i32,bool>{if i >=0{Ok(i)}else{Err(false)}}fnmain(){let r =f(10000);ifletOk(v)= r {println!("Ok: f(-1) = {}", v);}else{println!("Err");}}//运行结果:Ok: f(-1) = 10000
这里
r
的结果是
f
函数返回的
ok(10000)
,经过
if let
模式匹配后
v
的值为10000
*这段程序中函数
f
是错误的根源,现在我们再写一个传递错误的函数
g
:*
fng(i:i32)->Result<i32,bool>{let t =f(i);returnmatch t {Ok(i)=>Ok(i),Err(b)=>Err(b)};}
函数 g 传递了函数 f 可能出现的错误,这样写有些冗长,Rust 中可以在 Result 对象后添加
?
操作符将同类的
Err
直接传递出去:
fnf(i:i32)->Result<i32,bool>{if i >=0{Ok(i)}else{Err(false)}}fng(i:i32)->Result<i32,bool>{let t =f(i)?;Ok(t)// 因为确定 t 不是 Err, t 在这里已经推导出是 i32 类型}fnmain(){let r =g(10000);ifletOk(v)= r {println!("Ok: g(10000) = {}", v);}else{println!("Err");}}//运行结果:Ok: g(10000) = 10000
**
?
符的实际作用是将 Result 类非异常的值直接取出,如果有异常就将异常 Result 返回出去。所以? 符仅用于返回值类型为 Result<T, E> 的函数,且其中
E
类型必须和
?
所处理的 Result 的 E 类型一致。**
4、结合kind方法处理异常
虽然前面提到Rust 异常不像其他语言这么简单,但这并不意味着 Rust 实现不了:我们完全可以把
try
块在独立的函数中实现,将所有的异常都传递出去解决。
实际上这才是一个分化良好的程序应当遵循的编程方法:应该注重独立功能的完整性。
但是这样需要判断 Result 的 Err 类型,获取 Err 类型的函数是
kind()
做一个打开文件的实例:
usestd::io;usestd::io::Read;usestd::fs::File;fnread_text_from_file(path:&str)->Result<String,io::Error>{letmut f =File::open(path)?;letmut s =String::new();
f.read_to_string(&mut s)?;Ok(s)}fnmain(){let str_file =read_text_from_file("hello_rust.txt");match str_file {Ok(s)=>println!("{}", s),Err(e)=>{match e.kind(){io::ErrorKind::NotFound=>{println!("No such file");},
_ =>{println!("Cannot read the file");}}}}}//这里我没有创建hello_rust.txt文件,因此运行结果为:No such file
代码解释:
- 使用
read_text_from_file()
函数将文件打开的结果传给了str_file
变量- 这里并不存在hello_rust.txt
,因此File::open(path)?
不会打开文件,异常会存到f
中-f.read_to_string(&mut s)?
并不能读出文件内容,ok(s)
无内容 - 通过分析,分支会执行
Err(e)
的代码块,使用e.kind()
得到了错误类型并再次进行match
分支- 如果是NotFound
错误就会打印No such file- 其他情错误均提示Cannot read the file
Rust 的错误处理到此分享结束,欢迎大家指点,如有不恰当的地方还请不吝提出,让我们在交流中进步!
版权归原作者 微凉秋意 所有, 如有侵权,请联系我们删除。