本文节选自<<Rust语言圣经>>一书
欢迎大家加入Rust编程学院,一起学习交流:
QQ群:1009730433
可恢复的错误Result
还记得上一节中,提到的关于文件读取的思考题吧?当时我们解决了读取中如果遇到不可恢复错误该怎么处理,现在来看看,读取过程中,正常返回和遇到可以恢复的错误时该如何处理。
假设,我们有一台消息服务器,每个用户都通过websocket连接到该服务器来接收和发送消息,该过程就涉及到socket文件的读写,那么此时,如果一个用户的读写发生了错误,显然不能直接panic,否则服务器会直接崩溃,所有用户都会断开连接,因此我们需要一种更温和的错误处理方式: Result<T,E>
.
之前章节有提到过,Result<T,E>
是一个枚举类型,定义如下:
enum Result<T, E> {
Ok(T),
Err(E),
}
泛型参数T
代表成功时存入的正确值,存放方式是Ok(T)
,E
代表错误是存入的错误值,存放方式是Err(E)
,枯燥的讲解永远不及代码生动准确,因此先来看下打开文件的例子:
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
}
以上File::open
返回一个Result
类型, 那么问题来了:
如何获知变量类型或者函数的返回类型
有几种常用的方式:
- 第一种是查询标准库或者三方库文档,搜索
File
,然后找到它的open
方法,但是此处更推荐第二种方法:- 在Rust IDE章节,我们推荐了
VSCode
IED和rust-analyze
插件,如果你成功安装的话,那么就可以在VScode
中很方便的通过代码跳转的方式查看代码,同时rust-analyze
插件还会对代码中的类型进行标注,非常方便好用!- 你还可以尝试故意标记一个错误的类型,然后让编译器告诉你:
let f: u32 = File::open("hello.txt");
错误提示如下:
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | let f: u32 = File::open("hello.txt");
| ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum
`std::result::Result`
|
= note: expected type `u32`
found type `std::result::Result<std::fs::File, std::io::Error>`
上面代码,故意将f
类型标记成整形,编译器立刻不乐意了,你是在忽悠我吗?打开文件操作返回一个整形?来,大哥来告诉你返回什么:std::result::Result<std::fs::File, std::io::Error>
,我的天呐,怎么这么长的类型!
别慌,其实很简单,首先Result
本身是定义在std::result
中的,但是因为Result
很常用,就被包含在了prelude
中(将常用的东东提前引入到当前作用域内),因此无需手动引入std::result::Result
,那么返回类型可以简化为Result<std::fs::File,std::io::Error>
,你看看是不是很像标准的Result<T,E>
枚举定义?只不过T
被替换成了具体的类型std::fs::File
,是一个文件句柄类型,E
被替换成std::io::Error
,是一个IO错误类型.
这个返回值类型说明File::open
调用如果成功则返回一个可以进行读写的文件句柄,如果失败,则返回一个IO错误: 文件不存在或者没有访问文件的权限等。总之File::open
需要一个方式告知调用者是成功还是失败,并同时返回具体的文件句柄(成功)或错误信息(失败), 万幸的是,这些信息Result
枚举可以提供:
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
},
};
}
代码很清晰,对打开文件后的Result<T,E>
类型进行匹配取值,如果是成功,则将Ok(file)
中存放的的文件句柄file
赋值给f
,如果失败,则将Err(error)
中存放的错误信息error
使用panic
抛出来,进而结束程序,这非常符合上文提到过的panic
使用场景。
好吧,也没有那么合理:)
对返回的错误进行处理
直接panic
还是过于粗暴,因为实际上IO的错误有很多种,我们需要对部分错误进行特殊处理,而不是所有错误都直接崩溃:
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open(