【Rust自学】9.2. Result枚举与可恢复的错误 Pt.1:match、expect和unwrap处理错误

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=^・ω・^=)

9.2.1. Result枚举

通常情况下,错误都没有严重到需要停止整个程序的地步。某个函数之所以运行失败或者是遇到错误通常是由一些可以简单解释并做出响应的原因引起的。比如说程序要打开某个文件,但是这个文件并不存在,这个时候通常会考虑创建这个文件而不是直接终止程序。

Rust提供了Result这个枚举类型来处理这种可能失败的情况。它的定义是:

rust 复制代码
enum Result<T, E> {
	OK(T),
	Err(E),
}

它有两个泛型的类型参数,一个是T一个是E,它有两个变体,每个都关联了数据,OK关联了T,Err关联了E。泛型在第十章会讨论,现在只需要知道T是操作成功的情况下,OK变体里返回的数据的类型;E是在操作失败的情况下,Err变体里返回的错误的类型。

看个例子:

rust 复制代码
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)带入作用域的,在写代码时不需要额外的引入。如下例:

rust 复制代码
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!

rust 复制代码
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章才会讲。

rust 复制代码
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中的代码:

rust 复制代码
use std::fs::File;  
  
fn main() {  
    let f = File::open("6657.txt").unwrap();  
}

unwrap就相当于match表达式的快捷方法。它的缺点就是错误信息不可以自定义。

9.2.5. expect方法

那如果我想要unwrap的快捷性,又想要自定义错误信息怎么办?针对这种情况,Rust提供了expect方法,如果你有印象的话,在第一章的猜数游戏中就用过这种方法。

试试用expect重写unwrap的代码例:

rust 复制代码
use std::fs::File;  
  
fn main() {  
    let f = File::open("6657.txt").expect("file not found");  
}
相关推荐
佛系小嘟嘟11 分钟前
Android Jetpack Compose开发小组件【入门篇】
android·开发语言·android jetpack·小组件
开心工作室_kaic12 分钟前
springboot548二手物品交易boot代码(论文+源码)_kaic
前端·数据库·vue.js·后端·html5
Java知识日历1 小时前
【内含例子代码】Spring框架的设计模式应用(第二集)
java·开发语言·后端·spring·设计模式
尘浮生3 小时前
Java项目实战II基于微信小程序的家庭大厨(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven
军训猫猫头4 小时前
36.Add的用法 C#例子
开发语言·c#
暗碳5 小时前
cloudns二级免费域名python更新ipv6 dns记录
开发语言·python
milo.qu6 小时前
二、CSS基础
前端·javascript·css
个人开发-胡涂涂7 小时前
开源:软件世界的革命者
开发语言·开源
小周同学:7 小时前
elementui table 表格 分页多选,保持选中状态
前端·vue.js·elementui