【Rust自学】6.3. 控制流运算符-match

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=^・ω・^=)

6.3.1. 什么是match

match允许一个值与一系列模式进行匹配,并执行匹配的模式对应的代码。模式可以是字面值、变量名、通配符等等。

match表达式想象为硬币分类机:硬币沿着带有不同大小孔的轨道滑下,每枚硬币都会从它遇到的第一个适合的孔落下。以同样的方式,值会遍历match中的每个模式,并且在第一个模式中值"适合",该值落入要在执行期间使用的关联代码块中。

6.3.2. match的应用

来看个例子:编写一个函数,接受一枚未知的美国硬币,并以与计数机类似的方式确定它是哪种硬币并返回其价值(以美分为单位)。

rust 复制代码
enum Coin {
    Penny,// 1美分
    Nickel,// 5美分
    Dime,// 10美分
    Quarter,// 25美分
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}
  • match关键字后跟一个表达式,在本例中是值 coin 。这看起来与if中使用的条件表达式非常相似,但有一个很大的区别:if 的条件需要布尔值,但match可以是任何类型。本例中的coin类型是我们在第一行定义的Coin枚举。

  • 然后是花括号,花括号里有4个分支(英文叫arm),每个分支都是由待匹配的模式和它对应的代码来组成的。第一个分支Coin::Penny => 1,就使用Coin::Penny作为它的模式,中间的=>分隔模式和要运行的代码,这里要运行代码就是值:1,也就是返回1这个值。不同的分支之间使用,隔开。

  • match表达式执行时会把match后的表达式,在这里是coin,从上到下依次与里面的分支进行比较,如果模式与值匹配,则执行与该模式关联的代码。如果该模式与值不匹配,则继续执行下一个分支。匹配成功的分支所对应的代码表达式会作为整个match表达式的值进行返回。

    比如说match匹配到5美分,也就是Coin::Nickel上了,那么整个表达式的值就是5。又因为match表达式是value_in_cents这个函数中的最后一个表达式,所以它的值,也就是5,会作为函数的返回值。

  • 这里因为每个分支对应的代码都很简单,所以用=>就可以了,但如果一个分支对应的是多行代码,就需要用花括号把多行代码括起来。如下例:

rust 复制代码
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

6.3.3. 绑定值的模式

匹配的分支可以绑定到被匹配对象的部分值,通过这个功能,就可以从枚举类型的变体中提取值。

看个例子:一位朋友正在尝试收集全部 50 个州 25 美分。当我们按硬币类型对零钱进行分类时,我们还会标出与每个25美分相关的州名称(美国州太多了,这里就只写了Alabama和Alaska这两个州)

rust 复制代码
#[derive(Debug)] // 便于打印调试 
enum UsState {  
    Alabama,  
    Alaska,  
}  
  
enum Coin {  
    Penny,  
    Nickel,  
    Dime,  
    Quarter(UsState),  
}  
  
fn value_in_cents(coin: Coin) -> u8 {  
    match coin {  
        Coin::Penny => {  
            println!("Lucky penny!");  
            1  
        },  
        Coin::Nickel => 5,  
        Coin::Dime => 10,  
        Coin::Quarter(state) => {  
            println!("State quarter from {:?}!", state);  
            25  
        }  
    }  
}  
  
fn main() {  
    let c = Coin::Quarter(UsState::Alaska);  
    println!("{}", value_in_cents(c));  
}
  • 让25美分硬币(以下都叫Quarter)所对应的Coin里的变体,也就是Coin::Quarter给关联一个数据,它关联的数据就是上面的这个枚举类型UsState

  • value_in_cents函数里也需要对Quarter所在的分支稍微修改一下,匹配模式从Coin::Quarter修改到Coin::Quarter(state),意思就是把Coin::Quarter所关联的值绑定到state这个变量上,在后面的代码块里面就可以使用state这个变量,把Coin::Quarter所关联的值取出来进行使用。

    在某些情境下,Coin::Quarter所关联的值可能用不上,这时候就可以用通配符_代表不关心里面的内容:Coin::Quarter(_)

  • main函数里先声明了一个c变量,存的是Coin::Quarter(UsState::Alaska)。也就是存储了Coin::Quarter这个变体,然后其关联的值的是UsState::Alaska这个变体。最后又调用了一下value_in_cents函数。

看看输出效果:

State quarter from Alaska!
25

6.3.4. 匹配Option<T>

就以上一篇文章最后的代码例来做分析:

rust 复制代码
fn main() {  
    let x: i8 = 5;  
    let y: Option<i8> = Some(5);  
  
    let sum = match y {  
        Some(value) => x + value, // 如果 y 是 Some,则解包并相加  
        None => x,               // 如果 y 是 None,则返回 x    
    };  
}
  • 如果y不为None,就解包,把Some所关联的值绑定到value上,返回x + value的值
  • 如果yNone,就只返回x的值

6.3.5. match匹配必须穷举所有可能

Rust要求match覆盖到所有的可能性,这样才能保证代码的安全有效。

以上一个代码为基础稍作修改:

rust 复制代码
fn main() {  
    let x: i8 = 5;  
    let y: Option<i8> = Some(5);  
  
    let sum = match y {  
        Some(value) => x + value,   
    };  
}

输出:

error[E0004]: non-exhaustive patterns: `None` not covered
   --> src/main.rs:5:21
    |
5   |     let sum = match y {
    |                     ^ pattern `None` not covered
    |
note: `Option<i8>` defined here
   --> /Users/stanyin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:571:1
    |
571 | pub enum Option<T> {
    | ^^^^^^^^^^^^^^^^^^
...
575 |     None,
    |     ---- not covered
    = note: the matched value is of type `Option<i8>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
6   ~         Some(value) => x + value,
7   ~         None => todo!(),
    |

Rust捕捉到了None这个可能性没有被覆盖的错误,所以报错。把处理None的分支加上就没问题了。

如果可能性太多或者不想处理其中一些可能性,这个时候就可以使用通配符_

6.3.6. 通配符

首先要把想处理的分支照常写上,其他的使用通配符_代替即可。

看例子:v是一个u8类型的变量,判断v是否是0

rust 复制代码
use rand::Rng;  // 使用外部库
fn main(){  
    let v: u8 = rand::thread_rng().gen_range(0..=255);  // 生成随机数
    println!("{}", v);  
    match v {  
        0 => println!("zero"),  
        _ => println!("not zero"),  
    }  
}

u8有256个数256种可能,使用match自然是不可能每个数都写一个分支,所以,就可以为0写一个分支,其他的使用通配符_来代替。

输出:

136
not zero
相关推荐
m0_748251722 分钟前
ollama-webui - Ollama的ChatGPT 风格的 Web 界面
前端·chatgpt
小蒜学长14 分钟前
基于Spring Boot的宠物领养系统的设计与实现(代码+数据库+LW)
java·前端·数据库·spring boot·后端·旅游·宠物
L.S.V.15 分钟前
Java 溯本求源之基础(三十一)——泛型
java·开发语言
Redamancy_Xun22 分钟前
开源软件兼容性可信量化分析
java·开发语言·程序人生·网络安全·测试用例·可信计算技术
这我可不懂27 分钟前
低代码开发 实战转型案例一览
前端·低代码·程序员
明月看潮生33 分钟前
青少年编程与数学 02-005 移动Web编程基础 06课题、响应式设计
前端·青少年编程·编程与数学·移动web
明月看潮生34 分钟前
青少年编程与数学 02-005 移动Web编程基础 07课题、多媒体形式
前端·青少年编程·移动开发·编程与数学·移动web
ZLRRLZ35 分钟前
【C++】多态
开发语言·c++
べJL39 分钟前
SVG怎么画渐变甜甜圈(进度环)
前端·css
初遇你时动了情40 分钟前
css3滚动边框特效属性 filter、inset应用
前端·css·css3