Rust学习教程21 - Option和模式匹配

Posted 孙飞 Sunface

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust学习教程21 - Option和模式匹配相关的知识,希望对你有一定的参考价值。

本文节选自<<Rust语言圣经>>一书
欢迎大家加入Rust编程学院,一起学习交流:
QQ群:1009730433

解构Option

在枚举那一章,提到过Option枚举变量是用来解决Rust中一个变量是否有值的问题,定义如下:

enum Option<T> 
    Some(T),
    None,

总而言之,一个变量要么有值:Some(T), 要么为空: None.

那么现在的问题就是该如何去使用这个Option枚举类型,根据我们上一节的经验,可以通过match来实现。

匹配 Option<T>

使用Option<T>,是为了从 Some 中取出其内部的 T 值以及处理没有值的情况,为了演示这一点,下面一起来编写一个函数,它获取一个 Option<i32>,如果其中含有一个值,将其加一;如果其中没有值,则函数返回 None 值:

fn plus_one(x: Option<i32>) -> Option<i32> 
    match x 
        None => None,
        Some(i) => Some(i + 1),
    


let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

plus_one接受一个Option<i32>类型的参数,同时返回一个Option<i32>类型的值(这种形式的函数在标准库内随处所见),在该函数的内部处理中,如果传入的是一个None,则返回一个None切不做任何处理;如果传入的是一个Some(i32),则通过模式绑定,把其中的值绑定到变量i上,然后返回i+1的值,同时用Some进行包裹。

进一步说明,假设plus_one函数接受的参数值x是Some(5),那么接着我们将x与各个分支进行比较。

传入参数Some(5)

None => None,

首先是None分支,因为值Some(5) 并不匹配模式 None,所以继续匹配下一个分支。

Some(i) => Some(i + 1),

Some(5)Some(i) 匹配吗?当然匹配!它们是相同的成员。i 绑定了 Some 中包含的值,所以 i 的值是 5。接着匹配分支的代码被执行,所以我们将 i 的值加一并返回一个含有值 6 的新 Some

传入参数None

接着考虑下plus_one 的第二个调用,这次传入的xNone。我们进入 match 并与第一个分支相比较。

None => None,

匹配上了!接着程序继续执行该分支后的代码:返回表达式None的值,也就是返回一个None,因为第一个分支就匹配到了,其他的分支将不再比较。

模式适用场景

模式

模式是Rust中的特殊语法,它用来匹配类型中的结构和数据,它往往和match表达式联用,以实现强大的模式匹配能力。模式一般由以下内容组合而成:

  • 字面值
  • 解构的数组、枚举、结构体或者元组
  • 变量
  • 通配符
  • 占位符

所有可能用到模式的地方

match分支**

match VALUE 
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,

如上所示,match的每个分支就是一个模式,因为match匹配是穷尽式的,因此我们往往需要一个特殊的模式_,来匹配剩余的所有情况:

match VALUE 
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    _ => EXPRESSION,

if let分支

if let往往用于匹配一个模式,而忽略剩下的所有模式的场景:

if let Pattern = SOME_VALUE 


while let条件循环

一个与 if let 结构类似的是 while let 条件循环,它允许只要模式匹配就一直进行 while 循环。下面展示了一个使用while let的例子:

// Vec是动态数组
let mut stack = Vec::new();

// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);

// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() 
    println!("", top);

这个例子会打印出 32 接着是 1pop 方法取出动态数组的最后一个元素并返回 Some(value),如果动态数组是空的,它返回 None。对于while来说,只要 pop 返回 Some 就会一直不停的循环。一旦其返回 None``,while 循环停止。我们可以使用 while let 来弹出栈中的每一个元素。

你也可以用loop + if let 或者match来实现,但是会更加啰嗦。

for循环

let v = vec!['a', 'b', 'c'];

for (index, value) in v.iter().enumerate() 
    println!(" is at index ", value, index);

这里使用 enumerate 方法产生一个迭代器,该迭代器每次迭代会返回一个(索引,值)形式的元组,同时用(index,value)来匹配。

let语句

let PATTERN = EXPRESSION;

是的,这个语句我们已经用了无数次了,它也是一种模式匹配:

let x = 5;

这其中,x也是一种模式绑定,代表将匹配的值绑定到变量x上.因此,在Rust中,变量名也是一种模式,只不过它比较朴素很不起眼罢了。

let (x, y, z) = (1, 2, 3);

上面将一个元组与模式进行匹配(模式和值的类型比较相同!),然后把1,2,3分别绑定到x,y,z上。

因为模式匹配要求两边的类型必须相同,导致了下面的代码会报错:

let (x, y) = (1, 2, 3);

因为对于元组来说,元素个数也是类型的一部分!

函数参数

函数参数也是模式:

fn foo(x: i32) 
    // 代码

其中x就是一个模式,你还可以在参数中匹配元组:

fn print_coordinates(&(x, y): &(i32, i32)) 
    println!("Current location: (, )", x, y);


fn main() 
    let point = (3, 5);
    print_coordinates(&point);

&(3,5)会匹配模式&(x,y),因此x得到了3y得到了5.

if 和 if let

对于以下代码,编译器会报错:

let Some(x) = some_option_value;

因为右边的值可能为None,这种时候就不能进行匹配,也就是上面的代码遗漏了None的匹配。

类似letformatch都必须要求完全覆盖匹配,才能通过编译。

但是对于if let,就可以这样使用:

if let Some(x) = some_option_value 
    println!("", x);

因为if let允许匹配一种模式,而忽悠其余的模式。

以上是关于Rust学习教程21 - Option和模式匹配的主要内容,如果未能解决你的问题,请参考以下文章

Rust语言圣经21 - Option和模式匹配

匹配Rust中的Option静态字符串文字[duplicate]

rust match 模式匹配摘录

Rust学习 - Result/Option/unwrap/?

一天一门编程语言Rust 语言程序设计极简教程

Rust单链表