学习Rust时,注意到有4个概念经常放到一起讨论:Result、Option、unwapr和?操作符。 本文记录了我对这4个Rust概念的思考,这个思考过程帮助我理解并学会了如何写出更地道的Rust代码。

1、Option - 可空变量

虽然Rust中有null的概念,但是使用null并不是Rust中常见的模式。假设我们要 写一个函数,输入一种手机操作系统的名称,这个函数就会返回其应用商店的名称。 如果传入字符串iOS,该函数将返回App Store;如果传入字符串android,那么该函数将返回 Play Store。任何其他的输入都被视为无效。

在大多数开发语言中,我们可以选择返回null或字符串invalid来表示无效的结果, 不过这不是Rust的用法。

地道的Rust代码应该让该函数返回一个Option。Option或更确切的说Option<T> 是一个泛型,可以是Some<T>None(为了便于阅读,后续文章中将省略类型参数T)。 Rust将SomeNone称为变体(Variant) —— 这一概念在其他语言中并不存在,因此我也不 去定义到底什么是变体了。

在我们的示例中,正常情况下函数将返回包裹在Some变体中的字符串常量 App Store或Play Store。而在非正常情况下,函数将返回None。

1
2
3
4
5
6
7
fn find_store(mobile_os: &str) -> Option<&str> {match mobile_os {"iOS" => Some("App Store"),"android" => Some("Play Store"),_ => None}
}

要使用find_store(),我们可以用如下方式调用:

1
2
3
4
5
6
fn main() {println!("{}", match find_store("windows") {Some(s) => s,None => "Not a valid mobile OS"});
}

完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn find_store(mobile_os: &str) -> Option<&str> {match mobile_os {"iOS" => Some("App Store"),"android" => Some("Play Store"),_ => None}
}fn main() {println!("{}", match find_store("windows") {Some(s) => s,None => "Not a valid mobile OS"});
}

2、Result - 包含错误信息的结果

Result,或者更确切地说Result<T,E>,是和Rust中的Option相关的概念, 它是一个加强版本的Option。

Result<T, E>可能有以下结果之一:

  • Ok(T):结果为成员T
  • Err(E):结果为故障成员E

与之前我们看到Option可以包含Some或None不同,Result中包含了错误 相关信息,这是Option中所没有的。

让我们看一个函数实例,它返回一个Result。该函数摘自用于解析JSON字符串的 serde_json库,其签名为:

1
2
3
pub fn from_str<'a, T>(s: &'a str) -> Result<T, Error>
whereT: Deserialize<'a>,

假设我们要解析如下的字符串:

1
2
3
4
5
6
7
8
9
let json_string = r#"{"name": "John Doe","age": 43,"phones": ["+44 1234567","+44 2345678"]}"#;

目标是解析为Rust的一个person结构对象:

1
2
3
4
5
6
#[derive(Serialize, Deserialize)]
struct Person {name: String,age: u8,phones: Vec<String>,
}

解析过程的Rust代码如下:

1
2
3
4
let p:Person = match serde_json::from_str(json_string) {Ok(p) => p,Err(e) => ... //we will discuss what goes here next
};

正常情况下可以得到期望的结果。不过假设在输入的json_string中 有一个笔误,这导致程序运行时将执行Err分支。

当碰到Err时,我们可以采取两个动作:

  • panic!
  • 返回Err

3、unwrap - 故障时执行panic!

在上面的示例中,假设我们期望panic!:

1
2
3
4
let p: Person = match serde_json::from_str(data) {Ok(p) => p,Err(e) => panic!("cannot parse JSON {:?}, e"), //panic}

当碰到Err时,上面的代码panic!就会崩掉整个程序,也许这不是你期望的。 我们可以修改为:

1
let p:Person = serde_json::from_str(data).unwrap();

如果我们可以确定输入的json_string始终会是可解析的,那么使用unwrap 没有问题。但是如果会出现Err,那么程序就会崩溃,无法从故障中恢复。 在开发过程中,当我们更关心程序的主流程时,unwrap也可以作为快速 原型使用。

因此unwrap隐含了panic!。虽然与更显式的版本没有差异,但是危险在于 其隐含特性,因为有时这并不是你真正期望的行为。

无论如何,如果我们需要调用panic!,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
use serde::{Deserialize, Serialize};
use serde_json::Result;#[derive(Serialize, Deserialize)]
struct Person {name: String,age: u8,phones: Vec<String>,
}fn typed_example() -> Result<()> {//age2 is error on purposelet data = r#"{"name": "John Doe","age2": 43,"phones": ["+44 1234567","+44 2345678"]}"#;let p:Person = serde_json::from_str(data).unwrap();println!("Please call {} at the number {}", p.name, p.phones[0]);Ok(())
}fn main() {match typed_example() {Ok(_) => println!("program ran ok"),Err(_) => println!("program ran with error"),}
}

4、? - 故障时返回Err对象

当碰到Err时,我们不一定要panic!,也可以返回Err。不是每个Err都是不可恢复的, 因此有时并不需要panic!。下面的代码返回Err:

1
2
3
4
let p: Person = match serde_json::from_str(data) {Ok(p) => p,Err(e) => return Err(e.into()),
};

?操作符提供了一个更简洁的方法来替换上面的代码:

1
let p:Person = serde_json::from_str(data)?;

这时完整的Rust程序代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
use serde::{Deserialize, Serialize};
use serde_json::Result;#[derive(Serialize, Deserialize)]
struct Person {name: String,age: u8,phones: Vec<String>,
}fn typed_example() -> Result<()> {//age2 is error on purposelet data = r#"{"name": "John Doe","age2": 43,"phones": ["+44 1234567","+44 2345678"]}"#;let p: Person = serde_json::from_str(data)?;println!("Please call {} at the number {}", p.name, p.phones[0]);Ok(())
}fn main() {match typed_example() {Ok(_) => println!("program ran ok"),Err(e) => println!("program ran with error {:?}", e),}
}

5、使用unwrap和?解包Option

就像我们可以使用unwarp和?来处理Result,我们也可以使用unwrap和?来处理Option。

如果我们unwrap的Option的值是None,那么程序就会panic!。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn next_birthday(current_age: Option<u8>) -> Option<String> {// If `current_age` is `None`, this returns `None`.// If `current_age` is `Some`, the inner `u8` gets assigned to `next_age` after 1 is added to itlet next_age: u8 = current_age?;Some(format!("Next year I will be {}", next_age + 1))
}fn main() {let s = next_birthday(None);match s {Some(a) => println!("{:#?}", a),None => println!("No next birthday")}
}

原文链接:Taking the Unhappy Path with Result, Option, unwrap and ? operator in Rust

Rust学习 - Result/Option/unwrap/?相关推荐

  1. 理解Rust中的Result/Option/unwrap/?

    我在学习Rust时,注意到有4个概念经常放到一起讨论:Result.Option.unwapr和?操作符.本文记录了我对这4个Rust概念的思考,这个思考过程帮助我理解并学会了如何写出更地道的Rust ...

  2. Rust学习笔记(9)——Option的几个方法及所有权问题

    在rust的设计中,Option的设计非常巧妙,避免了其它语言中常见的Null引起的各种错误和异常.但Option与所有权的结合,尤其是在一些特定的数据结构,如链表.图等设计中,加上引用的各种约束,就 ...

  3. Rust学习日记番外篇——代码写诗

    Rust学习日记番外篇--代码写诗 中秋节即将来临啦~~提前祝大家月饼节快乐.今天看到了掘金的文章,有个代码写诗的活动,那我就小露一手了. 0x01 选定诗句 在掘金有下面几句诗可选. 举头望明月,低 ...

  4. rust 案例_Option 和 unwrap

    上个例子展示了如何主动地引入程序失败(program failure).当公主收到蛇这件不合适 的礼物时,我们就让程序 panic.但是,如果公主期待收到礼物,却没收到呢?这同样 是一件糟糕的事情,所 ...

  5. [Rust学习:四] Vec和栈

    [Rust学习:四] Vec和栈 一.前言 二.阅读Vec源码尝试常用接口. 1. 创建Vec 2. 在尾部添加push\extend\append 3. 任意位置添加insert 4. 在尾部删除p ...

  6. Rust学习:14_包和模块

    Rust学习:14_包和模块 前言 为了学习Rust,阅读了github上的Rust By Practice电子书,本文章只是用来记录自己的学习过程,感兴趣的可以阅读原书,希望大家都能掌握Rust! ...

  7. Scala学习之Option类

    今天特意学习一下Option类型 一般来说,对于每种语言都会有一个关键字来表示一个对象引用的"无".比如在Java中使用的是null. 在Scala中是融合了函数式编程的风格,当预 ...

  8. rust学习笔记中级篇1–泛型(霜之小刀)

    rust学习笔记中级篇1–泛型(霜之小刀) 欢迎转载和引用,若有问题请联系 若有疑问,请联系 Email : lihn1011@163.com QQ:2279557541 结构体泛型 首先上代码,如何 ...

  9. Rust学习—解决crates.io 仓库代码下载慢的问题

    今天是学习Rust的第二天,在尝试调用依赖(dependencies)的时候发现非!常!慢!需要采用一些方法来解决. Rust学习-解决crates.io 仓库代码下载慢的问题 今天在下载randra ...

最新文章

  1. php gridview,PHP编程:yii2-GridView在开发中常用的功能及技巧总结
  2. 字符串匹配(二)——逆向思维 BMH
  3. 【nacos系列】windows安装与配置nacos
  4. 关于ajax入门案例
  5. 默认适应窗口_PS教程基础之cs6默认快捷键汇总及补充
  6. Upload LABS Pass-8
  7. 将本地SQL Server数据库迁移到Azure SQL数据库
  8. Android View动画
  9. linux ubuntu美化,[linux] 我的ubuntu美化之路
  10. 服务器系统备份还原到虚拟机,一秒还原,一秒备份,系统重装「新手学识4」虚拟机--时光倒流...
  11. DAC杂谈二 ——ADC和DAC常用技术术语
  12. 如何制作移动端静态网页
  13. 江西省谷歌高清卫星地图下载
  14. 【第三方互联】12、支付宝(Alipay)授权第三方登录
  15. 驱动编程简单教程——PTC512(ADC芯片驱动)为例
  16. 揭秘刘德华感恩立志的少年时光
  17. mac连接服务器打不开网页,Mac电脑联接网络但是浏览器打不开网页
  18. 0. Redis-Server(操作)
  19. WIN10:今天开机突然遇到在打拼音的时候,输入框不见了,已下是本人的解决办法
  20. 启动阿里云上的Mysql报错:The server quit without updating PID file (/[FAILED]mysql/xxxx.pid)解决的另一途径

热门文章

  1. 计算机怎么c盘一键还原,怎么一键还原,小编教你怎样还原电脑系统
  2. “COMSOL 多物理场/FDTD 时域有限差分/ RSoft 光电器件仿真设计” 系列专题
  3. 跨境支付反洗钱业务逻辑和相关大数据分析技术实现
  4. 数据治理、共享交换、数据仓库、数据中心的关系
  5. 项目管理 王如龙老师 经典语录
  6. 论文中的参考文献自动编号及在正文中自动更新
  7. 淘宝装修教程 淘宝美工教程 淘宝教程 淘宝美工职业之路
  8. vivado仿真时,输出为高阻态
  9. 2014迅雷校园招聘笔试题
  10. 软阈值(Soft Thresholding) 函数解读