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. 亚马逊aws_access_key_id和aws_secret_access_key利用

    00X01 信息泄露 敏感信息泄露,例如环境变量.例如,为了配置AWS CLI,需要设置以下环境变量: $ export AWS_ACCESS_KEY_ID=AKISIOSFODNN7EXAMPLE ...

  2. 【MOOC】华中科技大学计算机组成原理慕课答案-第三章-运算方法与运算器

    待整理. 单选 1 原码除法是指 A. 操作数用绝对值表示,加上符号位后相除 √B. 操作数取绝对值相除,符号位单独处理 C. 操作数用原码表示,然后相除 D. 操作数用补码表示并进行除法,但商用原码 ...

  3. linux期末考试题(1)

    linux期末考试题 一.选择题(共20分,每小题2分) 1.以下哪个环境变量表示shell搜索外部命令或程序路径(C) A.ENV B.PWD C.PATH D.ROOT 解答: ENV用于显示当前 ...

  4. 使用C#构建一个同时问多个LLM并总结的小工具

    前言 在AI编程时代,如果自己能够知道一些可行的解决方案,那么描述清楚交给AI,可以有很大的帮助. 但是我们往往不知道真正可行的解决方案是什么? 我自己有过这样的经历,遇到一个需求,我不知道有哪些解决 ...

  5. IDEA开启热加载

    然後 Ctrl+Shift+Ait+/ pom.xml里添加 <build> <plugins> <!-- 配置插件,让热部署依赖spring-boot-devtools ...

  6. Number of Digit One——LeetCode⑩

    //原题链接https://leetcode.com/problems/number-of-digit-one/ 题目描述 Given an integer n, count the total nu ...

  7. Win32汇编学习笔记05

    定位关键点3种方法: 过程函数 api 字符串 但是不确定用要哪一种方法,可以3种方法都用一下,因为在不同的程序,实用的方法是不一样的 窗口程序看控件信息 1.通过OD去看 还可以用 spy ++ 查 ...

  8. Java中判断某一字符串是否包含数字、字母和中文

         在Java中判断某一字符串是否为纯英文.纯数字.英文和数字的组合等时,通常使用正则str.matches匹配,告诉这个字符串是否与给定的正则表达式匹配.  各种字符的unicode编码的范围 ...

  9. uniapp中使用mqtt.js的踩坑记录

    最近在uniapp的vue3.0版本中使用mqtt.js库时遇到了一些坑,经过亲身踩坑,现在把实际能够实现在uniapp的app端能够使用mqtt.js的方法步骤记录如下: 一.安装 首先安装mqtt ...

  10. tkinter使用pyinstaller 打包报错,ModuleNotFoundError: No module named ‘babel.numbers‘

    @ 目录 报错原因 解决办法 报错原因 导入的tkcalendar 包 中,模块名与原生的冲突 from tkcalendar import DateEntry 解决办法 打包时加入参数--hidde ...