Result枚举在Rust中是使用频率极高的一个类型,常用于函数的返回值定义,其源码如下:

#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
#[rustc_diagnostic_item = "result_type"]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Result<T, E> {
/// Contains the success value
#[lang = "Ok"]
#[stable(feature = "rust1", since = "1.0.0")]
Ok(#[stable(feature = "rust1", since = "1.0.0")] T), /// Contains the error value
#[lang = "Err"]
#[stable(feature = "rust1", since = "1.0.0")]
Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}

抛开一堆#开头的trait不看,核心就是Ok及Err这2个泛型成员。

先来看一个基本示例:

    let result_ok: Result<String, String> = Result::Ok(String::from("success"));
let result = match result_ok {
Result::Ok(o) => o,
Result::Err(e) => e,
};
println!("{}", result);

这里定义了一个"成功"的Result,然后使用模式匹配对其进行处理,如果是Ok的,取出Ok的值,否则取出Err的值。这类简单重复的判断,经常会用到,rust封装了1个等效的方法:unwrap,其内部实现如下:

    pub fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
}
}

其实就是模式匹配,取出Ok或Err的值。所以前面这个示例,可以改写成:

    let result_ok: Result<String, String> = Result::Ok(String::from("success"));
// let result = match result_ok {
// Result::Ok(o) => o,
// Result::Err(e) => e,
// };
let result = result_ok.unwrap();
println!("{}", result);

unwrap源码中的unwrap_failed继续追下去的话,可以看到:

fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! {
panic!("{}: {:?}", msg, error)
}

调用了panic方法,这意味着如果Result返回的是Err,则程序会崩溃,可以试一把:

如果Err发生时不希望程序崩溃,可以使用unwrap_or()

    let result_fail: Result<String, String> = Result::Err(String::from("failure"));
let result = result_fail.unwrap_or(String::from("err occur"));
println!("{}", result);

unwrap_or可以传入一个default缺省的错误值,上面这段将输出“err occur”。但这样一来,就把原始的错误信息failure给丢失了! 不用担心,rust早考虑到了:

    let result_fail: Result<String, String> = Result::Err(String::from("failure"));
let result = result_fail.unwrap_or_else(|e|e);
println!("{}", result);

使用unwrap_or_else传入1个闭包匿名函数,可以随心所欲的对原始错误进行处理,这里我们啥也没干,|e|e,表示原样返回。

Result枚举还提供了其它一些常用方法,参见上图,有兴趣的同学,可以研究下源码。

最后来看一个稍微复杂点的示例:在当前目录下,打开hello.txt文件,如果该文件不存在,则自动创建一个空的hello.txt。

use core::panic;
use std::fs::File;
use std::io::{ErrorKind}; fn main() {
let file_name = String::from("hello.txt"); //第1层match
match File::open(&file_name) {
Ok(file) => file,
//第2层match
Err(error) => match error.kind() {
//第3层match
ErrorKind::NotFound => match File::create(&file_name) {
Ok(fc) => fc,
Err(e) => panic!("Error creating file:{:?}", e),
},
oe => panic!("Error opening the file:{:?}", oe),
},
};
}

用了3层模式匹配(match套娃),看上去比较原始,如果不喜欢这种match写法,可以用今天学到的知识,换成相对“正常点”的写法:

    File::open(&file_name).unwrap_or_else(|e| {
if e.kind() == ErrorKind::NotFound {
File::create(&file_name).unwrap_or_else(|e| {
panic!("Error creating file:{:?}", e);
})
} else {
panic!("Error opening file:{:?}", e)
}
});

Rust程序员可能会写得更简短:

    File::open(&file_name).unwrap_or_else(|e| match e.kind() {
ErrorKind::NotFound => {
File::create(&file_name).unwrap_or_else(|e| panic!("Error creating file:{:?}", e))
}
_ => panic!("Error opening file:{:?}", e)
});

Rust中的Result枚举的更多相关文章

  1. 【译】理解Rust中的Futures(二)

    原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...

  2. Rust 中的数据布局--非正常大小的类型

    非正常大小的类型 大多数的时候,我们期望类型在编译时能够有一个静态已知的非零大小,但这并不总是 Rust 的常态. Dynamically Sized Types (DSTs) Rust 支持动态大小 ...

  3. Rust 中的类型转换

    1. as 运算符 as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32 .i64 .f32 . f64 . u8 . u32 . char 等类型),并且它是安全的. 例 ...

  4. Rust中的RefCell和内部可变性

    RefCell Rust在编译阶段会进行严格的借用规则检查,规则如下: 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用. 引用必须总是有效. 即在编译阶段,当有一个不可变值时,不能可 ...

  5. 【译】理解Rust中的Futures (一)

    原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...

  6. 【译】深入理解Rust中的生命周期

    原文标题:Understanding Rust Lifetimes 原文链接:https://medium.com/nearprotocol/understanding-rust-lifetimes- ...

  7. Rust 中的数据布局-repr

    repr(Rust) 首先,所有类型都有一个以字节为单位的对齐方式,一个类型的对齐方式指定了哪些地址可以用来存储该值.一个具有对齐方式n的值只能存储在n的倍数的地址上.所以对齐方式 2 意味着你必须存 ...

  8. Rust中的derive属性详解

    1. Rust中的derive是什么? 在Rust语言中,derive是一个属性,它可以让编译器为一些特性提供基本的实现.这些特性仍然可以手动实现,以获得更复杂的行为. 2. derive的出现解决了 ...

  9. Rust中的宏:声明宏和过程宏

    Rust中的声明宏和过程宏 宏是Rust语言中的一个重要特性,它允许开发人员编写可重用的代码,以便在编译时扩展和生成新的代码.宏可以帮助开发人员减少重复代码,并提高代码的可读性和可维护性.Rust中有 ...

  10. [Effective JavaScript 笔记]第47条:绝不要在Object.prototype中增加可枚举的属性

    之前的几条都不断地重复着for...in循环,它便利好用,但又容易被原型污染.for...in循环最常见的用法是枚举字典中的元素.这里就是从侧面提出不要在共享的Object.prototype中增加可 ...

随机推荐

  1. 1.4K star!几分钟搞定AI视频创作,这个开源神器让故事可视化如此简单!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 story-flicks 是一个基于AI技术的自动化视频生成工具,能够将文字剧本快速转化为高 ...

  2. 阿里云Ansible自动化运维平台部署

    以下是在阿里云平台上基于Ansible实现自动化运维的完整实践指南,整合所有核心操作流程和命令,适配指定的服务器规划: 一.环境规划 主机名 IP地址 角色 操作系统 manage01 192.168 ...

  3. Mysql 常用时间函数(上)

    也是做数据分析嘛, SQL 必然是每天都要用的, 然后在分析中, 时间是数据分析中极为重要的部分, 可以说是承上启下或者是贯穿整个分析. 比如, 日, 周, 月, 季度, 年度 与之相对应的 环比, ...

  4. TVM 安卓环境搭建部署

    安装VULKAN 参考:https://blog.csdn.net/luolinll1212/article/details/113261022 在编译TVM,当config.cmake中将USE_V ...

  5. win10无选字框

    设置-->时间与语言-->语言-->中文-->选项 下滑到最底 微软拼音-->选项 常规-->(下滑到最底)打开使用以前版本的微软拼音输入法-->确定

  6. java从小白到老白③

    PS:①小陌笔记中蓝色紫色等一切花哨字体皆用来引入知识点(废话流),可忽略不计 . ②黑字正文小陌竭力向言简意赅靠近再靠近. ③红色字体小陌觉得重要的地方 老规矩,题目引入: int a = 1; i ...

  7. Disruptor—2.并发编程相关简介

    大纲 1.并发类容器 2.volatile关键字与内存分析 3.Atomic系列类与UnSafe类 4.JUC常用工具类 5.AQS各种锁与架构核心 6.线程池的最佳使用指南 1.并发类容器 (1)C ...

  8. linux下用android-file-transfer-linux替换掉 gvfs-mtp来挂载mtp

    用android-file-transfer-linux替换掉 gvfs-mtp来挂载mtp 问题现状 在我使用gvfs-mtp挂载的时候,无论读取文件还是获取对应文件夹下所有文件的路径等操作,都非常 ...

  9. Innosetup 安装 VC_redist 运行时库

    #普通安装vc_redis.x86.exe(会提示用户做出选择),在innosetup的[Run]属性中添加下面这一行 Filename: "{app}VC_redist.x86.exe&q ...

  10. 卡掉hash的方法

    大质数hash 通常,这个质数会选择在 \(10^9\) 附近,如 \(998244353\),\(10^9+7\). 考虑生日碰撞,欲达到 50% 成功率,需要尝试的次数为 \[\begin{ali ...