喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
9.2.1. Result枚举
通常情况下,错误都没有严重到需要停止整个程序的地步。某个函数之所以运行失败或者是遇到错误通常是由一些可以简单解释并做出响应的原因引起的。比如说程序要打开某个文件,但是这个文件并不存在,这个时候通常会考虑创建这个文件而不是直接终止程序。
Rust提供了Result这个枚举类型来处理这种可能失败的情况。它的定义是:
enum Result<T, E> {OK(T),Err(E),
}
它有两个泛型的类型参数,一个是T
一个是E
,它有两个变体,每个都关联了数据,OK
关联了T
,Err
关联了E
。泛型在第十章会讨论,现在只需要知道T
是操作成功的情况下,OK
变体里返回的数据的类型;E
是在操作失败的情况下,Err
变体里返回的错误的类型。
看个例子:
use std::fs::File;
fn main() { let f = File::open("6657.txt");
}
这个代码的操作是打开文件,但这个文件不一定存在,所以说函数的运行可能会失败,所以File::open
这个函数的返回值是Result枚举。这个Result里面第一个参数是std::fs::File
,也就是文件类型(成功的时候返回);而第二个是std::io::Error
,也就是io错误(失败的时候返回)
9.2.2. 用match处理Result
和Option
枚举一样,Result及其变体也是由预导入模块(prelude)带入作用域的,在写代码时不需要额外的引入。如下例:
use std::fs::File;
fn main() { let f = File::open("6657.txt"); let f = match f { Ok(file) => file, Err(e) => panic!("Error: {}", e), };
}
如果返回的值是Ok
,那么就把它所关联的值绑定到file
上再返回回去赋给f
;如果返回的值是Err
,那么就把错误信息绑定在e
上由panic!
宏打印出来并停止程序。
9.2.3. 匹配不同的错误
我们把上面的例子完善一下,如果出现找不到的情况,就创建这个文件,如果连创建都不成功或者是出现了除了找不到意外的情况(比如没有权限打开),才触发panic!
。
use std::fs::File;
use std::io::ErrorKind; fn main() { let f = File::open("6657.txt"); let f = match f { Ok(file) => file, Err(e) => match e.kind() { ErrorKind::NotFound => match File::create("6657.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating file: {:?}", e), }, other_error => panic!("Problem opening file: {:?}", other_error), }, };
}
- 在最外层仍然是如果
f
的值是Ok
就把文件内容返回给f
- 但在处理
Err
变体时有不同,Err
所附带的数据的类型是std::io::Error
,这个struct
上有一个.kind()
方法,通过这个方法可以获得std::io::ErrorKind
这个类型值,它也是一个枚举,也是由标准库提供的,它里面的变体是用来描述io
操作可能引起的不同错误 ErrorKind
里面有一个变体ErrorKind::NotFound
,表示文件不存在,这个时候就应该创建文件,在下面我们再讨论创建文件。除了ErrorKind::NotFound
,也可能有其他错误(比如说没有权限读取),这里把其他错误赋给了other_error
这个值,并由panic!
打印出来然后停止程序。- 对于创建文件,可以使用
File::create()
这个函数,其参数就是文件名。而创建文件本身也有可能会失败(比如没权限),所以File::create()
的返回值也是Result类型,那就再用一个match
表达式来处理,如果是OK
(创建成功),那就把OK
关联的值,也就是这个新创建的文件呢的内容(内容肯定是空的,因为是新创建的)绑定在fc
这个变量上返回赋给f
;如果是Err
(创建失败),就把Err
所关联的错误信息绑定在e
上用panic!
打印出来并停止程序。
match
确实用的比较多,但也比较原始,这里的套娃是其可读性大大降低(虽然对比其他语言可能可读性还要高一些)。而在第13章会讲一个概念叫做闭包(closure),Result类型有很多方法接受闭包作为参数,而且这些方法都是使用match
实现的,能够使得代码更加简洁,我把使用闭包的代码例写在这里,但现在不会讲,到13章才会讲。
use std::fs::File;
use std::io::ErrorKind;fn main() {let greeting_file = File::open("6657.txt").unwrap_or_else(|error| {if error.kind() == ErrorKind::NotFound {File::create("6657.txt").unwrap_or_else(|error| {panic!("Problem creating the file: {error:?}");})} else {panic!("Problem opening the file: {error:?}");}});
}
9.2.4. unwrap方法
match
表达式确实灵活有用,但写出来的代码也确实复杂了一些,而Result这个枚举类型本身也定义了许多的辅助方法来应对各式各样的任务,其中有一个常用的方法叫unwrap
。
如果unwrap
接收到了OK
,那么它就会把OK
附带的值返回;而如果接收到了Err
,那么unwrap
就会调用panic!
宏。比如说用unwrap
重写9.2.2中的代码:
use std::fs::File; fn main() { let f = File::open("6657.txt").unwrap();
}
unwrap
就相当于match
表达式的快捷方法。它的缺点就是错误信息不可以自定义。
9.2.5. expect方法
那如果我想要unwrap
的快捷性,又想要自定义错误信息怎么办?针对这种情况,Rust提供了expect
方法,如果你有印象的话,在第一章的猜数游戏中就用过这种方法。
试试用expect
重写unwrap
的代码例:
use std::fs::File; fn main() { let f = File::open("6657.txt").expect("file not found");
}