当前位置: 首页 > article >正文

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

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

6.3.1. 什么是match

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

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

6.3.2. match的应用

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

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,会作为函数的返回值。

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

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这两个州)

#[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>

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

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覆盖到所有的可能性,这样才能保证代码的安全有效。

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

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

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

http://www.kler.cn/a/452538.html

相关文章:

  • Linux -- 互斥的底层实现
  • 理解并使用 Linux 内核的字符设备
  • PVE虚拟化平台之开启虚拟机IP显示方法
  • vue中proxy代理配置(测试一)
  • C#调用OpenXml,读取excel行数据,遇到空单元跳过现象处理
  • whisper.cpp: PC端测试 -- 电脑端部署音频大模型
  • 【免费分享】mysql笔记,涵盖查询、缓存、存储过程、索引,优化。
  • 代码随想录day25 | leetcode 491.递增子序列 46.全排列 回溯总结
  • Grok 2.0:马斯克的大模型挑战ChatGPT,AI竞争再升级
  • 【玩转MacBook】Maven安装
  • 大数据之 HDFS:特性与架构
  • Lua语言入门 - Lua 面向对象
  • Excel粘贴复制不完整的原因以及解决方法
  • 在git commit之前让其自动执行一次git pull命令
  • [python SQLAlchemy数据库操作入门]-05.插入数据:记录单笔股票交易信息
  • 【学习总结|DAY023】Java高级技术
  • SpringBoot统计接口请求耗时
  • Docker 部署 plumelog 最新版本 实现日志采集
  • 【前端必读】如何免费无限使用Cursor:AI编程工具的终极指南!
  • Merry Christmas HTML
  • Redis——缓存击穿
  • NetLimiter使用教程,并掌握其基本的网络管理和流量控制能力
  • 聊一聊 C#线程池 的线程动态注入 (下)
  • Flutter项目兼容鸿蒙Next系统
  • 外包干了27天,技术退步明显。。。。。
  • MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)