rust学习-result/option/unwrap/?(代码片段)

跨链技术践行者 跨链技术践行者     2022-12-14     419

关键词:

学习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> 
where
    T: 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 purpose
    let 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 purpose
    let 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 it
    let 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

c++&rust对比c++学习和运用rust语言系列文章

接下来,会出一系列文章,通过比较C++与Rust的异同点,更好的学习r和运用Rust语言! 查看详情

c++&rust对比c++学习和运用rust语言系列文章

接下来,会出一系列文章,通过比较C++与Rust的异同点,更好的学习r和运用Rust语言! 查看详情

c++&rust对比c++学习和运用rust语言系列文章

接下来,会出一系列文章,通过比较C++与Rust的异同点,更好的学习r和运用Rust语言! 查看详情

c++&rust对比c++学习和运用rust语言系列文章

接下来,会出一系列文章,通过比较C++与Rust的异同点,更好的学习r和运用Rust语言! 查看详情

rust学习1(代码片段)

1.为什么学习rust官网的解释如下,性能好,可靠,生产力。我的理由,想看看怎么保证内存安全与线程安全的 1.环境搭建去网站下载对应的rustup-init   https://forge.rust-lang.org/infra/other-installation-methods.html然后按照操作... 查看详情

rust编译器教我做人,为啥还要学习rust语言,因为想使用rust做一些底层服务,更深入的研究技术。

目录1,继续学习Rust语言,确实学习成本很高,学了两周还在学习入门概念,和编译器斗争2,rust学习曲线非常高,为啥还要坚持学习,一直想写一些服务研究研究底层的技术啥的3,rust对前端也有... 查看详情

rust语言:开始学习rust语言,使用vscode进行开发,rust不要做为自己的第一门开发语言,c++和rust都要学习好,成年人两个都要。(代码片段)

目录前言1,关于Rust学习,对比了一段时间还是决定学习了2,官方网站3,使用Cargo创建项目和进行包管理,4,增加项目依赖包,和nodejs比较像5,另外一个tauriapp框架,后台也是用rust开发的前言... 查看详情

rust编译器教我做人,为啥还要学习rust语言,因为想使用rust做一些底层服务,更深入的研究技术。

目录1,继续学习Rust语言,确实学习成本很高,学了两周还在学习入门概念,和编译器斗争2,rust学习曲线非常高,为啥还要坚持学习,一直想写一些服务研究研究底层的技术啥的3,rust对前端也有... 查看详情

rust学习(代码片段)

Rust  1.install  2.playonline  curlhttps://sh.rustup.rs-sSf|shecho‘PATH="$PATH:$HOME/.cargo/bin"‘>>~/.bashrcrustupdoc Tutorialorglearn (entry)   查看详情

rust学习

# Rust学习## Rust安装1. 直接访问官网 https://www.rust-lang.org/tools/install 下载相应的版本进行安装。2. 如果是Windows版本的Rust,需要安装Visual Studio 2012以上版本的VC++ 才能编译通过3. 在命令行窗口中... 查看详情

rust学习教程14-函数(代码片段)

...t语言圣经>>一书欢迎大家加入Rust编程学院,一起学习交流:QQ群:1009730433函数Rust的函数我们在之前已经见过不少,跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快,骚年们,请珍惜这种... 查看详情

rust学习教程14-函数(代码片段)

...t语言圣经>>一书欢迎大家加入Rust编程学院,一起学习交流:QQ群:1009730433函数Rust的函数我们在之前已经见过不少,跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快,骚年们,请珍惜这种... 查看详情

rust语言:rust项目有很多公司程序员已经开始在学习使用中了,都有rustmeetup2022了,想往底层走的可以学习c++rust了。

Rust权威指南:英文版本:https://doc.rust-lang.org/stable/book/中文翻译版本:https://kaisery.github.io/trpl-zh-cn/也有pdf版本可以下载的。实践证明,对于大规模的开发团队,即使成员的系统编程水平不同,Rust仍是高效的协作工具。底... 查看详情

rust学习教程28-深入类型转换(代码片段)

...t语言圣经>>一书欢迎大家加入Rust编程学院,一起学习交流:QQ群:1009730433类型转换Rust是类型安全的语言,因此在Rust中做类型转换不是一件简单的事,这一章节我们将对Rust中的类型转换进行详尽讲解。as转换先... 查看详情

rust学习-result/option/unwrap/?(代码片段)

学习Rust时,注意到有4个概念经常放到一起讨论:Result、Option、unwapr和?操作符。本文记录了我对这4个Rust概念的思考,这个思考过程帮助我理解并学会了如何写出更地道的Rust代码。1、Option-可空变量虽然Rust中有null的... 查看详情

#yyds干货盘点#为什么要学习rust?

1.Rust相关概念与简介Rust是一种令人兴奋的新兴编程语言,它可以让每个人编写可靠且高效的软件。Rust可以用来替换C/C++,Rust和他们具有相同的性能,但是很多常见的bug在编译时就可以被消灭。Rust是一种通用的编程语言,但是它... 查看详情

rust学习教程26-特征对象(代码片段)

...t语言圣经>>一书欢迎大家加入Rust编程学院,一起学习交流:QQ群:1009730433特征对象在上一节中有一段代码无法通过编译:fnreturns_summarizable(switch:bool)->implSummaryifswitchPost//...elseWeibo//...其中Post和Weibo都实现了 查看详情

rust学习教程26-特征对象(代码片段)

...t语言圣经>>一书欢迎大家加入Rust编程学院,一起学习交流:QQ群:1009730433特征对象在上一节中有一段代码无法通过编译:fnreturns_summarizable(switch:bool)->implSummaryifswitchPost//...elseWeibo//...其中Post和Weibo都实现了 查看详情