news 2026/9/6 12:40:14

【Rust入门知识点学与练】第14课:模式匹配 Pattern Matching

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Rust入门知识点学与练】第14课:模式匹配 Pattern Matching

知识点1:match 穷举匹配

match 是 Rust 最强大的控制流工具,必须穷举所有可能的情况:

fndescribe_number(n:i32)->String{matchn{0=>String::from("零"),1..=9=>String::from("个位数"),// 1到910..=99=>String::from("两位数"),// 10到99100..=999=>String::from("三位数"),// 100到999_=>String::from("很大的数"),// _ 匹配所有其他情况}}fnmain(){println!("{}",describe_number(0));// 零println!("{}",describe_number(7));// 个位数println!("{}",describe_number(42));// 两位数println!("{}",describe_number(500));// 三位数println!("{}",describe_number(1000));// 很大的数}

知识点2:match 解构枚举和结构体

enumCommand{Echo(String),Move{x:i32,y:i32},Quit,}structPoint{x:i32,y:i32,}fnmain(){// 匹配枚举letcmd=Command::Move{x:10,y:20};matchcmd{Command::Echo(msg)=>println!("回声: {}",msg),Command::Move{x,y}=>println!("移动到 ({}, {})",x,y),Command::Quit=>println!("退出"),}// 匹配结构体letp=Point{x:3,y:7};matchp{Point{x:0,y}=>println!("在Y轴上, y={}",y),Point{x,y:0}=>println!("在X轴上, x={}",x),Point{x,y}=>println!("普通点 ({}, {})",x,y),}// 匹配元组letpair=(1,2);matchpair{(0,_)=>println!("第一个是0"),(_,0)=>println!("第二个是0"),(x,y)=>println!("两个数: {} 和 {}",x,y),}}

知识点3:匹配守卫(Match Guard)

在模式后面加 if 条件,进一步过滤:

fnclassify(n:i32)->&'staticstr{matchn{xifx<0=>"负数",0=>"零",xifx>0&&x<=100=>"小的正数",xifx>100=>"大的正数",_=>"其他",}}fnmain(){println!("{}",classify(-5));// 负数println!("{}",classify(0));// 零println!("{}",classify(42));// 小的正数println!("{}",classify(200));// 大的正数}

知识点4:if let 和 while let

当你只关心一种匹配情况时,用 if let 更简洁:

fnmain(){letvalue:Option<i32>=Some(42);// 用 match(啰嗦)matchvalue{Some(v)=>println!("值是 {}",v),_=>{}}// 用 if let(简洁)ifletSome(v)=value{println!("值是 {}",v);}// if let 也可以加 elseletempty:Option<i32>=None;ifletSome(v)=empty{println!("值是 {}",v);}else{println!("没有值");}// while let:循环直到匹配失败letmutstack=vec![1,2,3,4,5];whileletSome(top)=stack.pop(){println!("弹出: {}",top);}// 输出: 5, 4, 3, 2, 1(逐个弹出直到栈空)}

知识点5:let else 和 @ 绑定

fnmain(){// let else:匹配失败时执行 else 分支(必须 diverge:return/break/panic)letvalue:Option<i32>=Some(42);letSome(v)=valueelse{println!("没有值,提前返回");return;};println!("拿到值: {}",v);// 42// @ 绑定:匹配的同时把整个值绑定到一个变量letmsg=String::from("hello");matchmsg.len(){n@1..=5=>println!("短字符串,长度={}",n),n@6..=10=>println!("中等字符串,长度={}",n),n=>println!("长字符串,长度={}",n),}// @ 绑定在枚举中letvalue:Option<i32>=Some(42);matchvalue{Some(n@1..=100)=>println!("1到100之间的数: {}",n),Some(n)=>println!("其他数: {}",n),None=>println!("没有值"),}}

核心规则

概念 写法
穷举匹配 match value { pattern => expr, … }
通配符 _ => …
范围匹配 1…=9
匹配守卫 x if x > 0 => …
单分支匹配 if let Pattern = value { … }
循环匹配 while let Pattern = value { … }
匹配失败分支 let Pattern = value else { … };
绑定匹配值 n @ pattern => …
解构结构体 Struct { field1, field2 }
解构枚举 Enum::Variant(data)

动手试试

补全下面的代码:

enumExpr{Num(f64),Add(Box<Expr>,Box<Expr>),Sub(Box<Expr>,Box<Expr>),Mul(Box<Expr>,Box<Expr>),Div(Box<Expr>,Box<Expr>),}// 补全:实现 eval 函数,递归计算表达式的值// 除法遇到除数为0时返回 Nonefneval(expr:&Expr)->Option<f64>{// 补全这里// 提示:用 match 匹配每种情况,递归调用 eval// Add: a + b// Sub: a - b// Mul: a * b// Div: a / b(b为0返回None)// 用 if let 或 match 处理递归结果中可能出现 None 的情况todo!()}// 补全:实现 describe 函数,返回表达式的字符串描述// 例如: Add(Num(1), Num(2)) => "(1 + 2)"// Mul(Num(3), Add(Num(4), Num(5))) => "(3 * (4 + 5))"fndescribe(expr:&Expr)->String{// 补全这里todo!()}fnmain(){// 表达式: (3 + 4) * 2letexpr=Expr::Mul(Box::new(Expr::Add(Box::new(Expr::Num(3.0)),Box::new(Expr::Num(4.0)),)),Box::new(Expr::Num(2.0)),);// 补全:打印表达式描述和计算结果// 期望输出:// 表达式: ((3 + 4) * 2)// 结果: Some(14)// 表达式: 10 / (5 - 5)letdiv_zero=Expr::Div(Box::new(Expr::Num(10.0)),Box::new(Expr::Sub(Box::new(Expr::Num(5.0)),Box::new(Expr::Num(5.0)),)),);// 补全:打印表达式描述和计算结果// 期望输出:// 表达式: (10 / (5 - 5))// 结果: None}

期望输出:
表达式: ((3 + 4) * 2)
结果: Some(14)
表达式: (10 / (5 - 5))
结果: None

写好了贴过来,没问题就进入第15课:泛型 Generics 👋

贴代码

下面给出完整可运行代码, eval 和 describe 全部补全,并附带讲解。

enumExpr{Num(f64),Add(Box<Expr>,Box<Expr>),Sub(Box<Expr>,Box<Expr>),Mul(Box<Expr>,Box<Expr>),Div(Box<Expr>,Box<Expr>),}// 递归计算表达式,除数为0返回None,子表达式出错也返回Nonefneval(expr:&Expr)->Option<f64>{matchexpr{Expr::Num(n)=>Some(*n),Expr::Add(a,b)=>{letva=eval(a)?;letvb=eval(b)?;Some(va+vb)}Expr::Sub(a,b)=>{letva=eval(a)?;letvb=eval(b)?;Some(va-vb)}Expr::Mul(a,b)=>{letva=eval(a)?;letvb=eval(b)?;Some(va*vb)}Expr::Div(a,b)=>{letva=eval(a)?;letvb=eval(b)?;ifvb==0.0{None}else{Some(va/vb)}}}}// 生成带括号的表达式字符串fndescribe(expr:&Expr)->String{matchexpr{Expr::Num(n)=>format!("{}",n),Expr::Add(a,b)=>format!("({} + {})",describe(a),describe(b)),Expr::Sub(a,b)=>format!("({} - {})",describe(a),describe(b)),Expr::Mul(a,b)=>format!("({} * {})",describe(a),describe(b)),Expr::Div(a,b)=>format!("({} / {})",describe(a),describe(b)),}}fnmain(){// 表达式: (3 + 4) * 2letexpr=Expr::Mul(Box::new(Expr::Add(Box::new(Expr::Num(3.0)),Box::new(Expr::Num(4.0)),)),Box::new(Expr::Num(2.0)),);println!("表达式: {}",describe(&expr));println!("结果: {:?}",eval(&expr));// 表达式: 10 / (5 - 5)letdiv_zero=Expr::Div(Box::new(Expr::Num(10.0)),Box::new(Expr::Sub(Box::new(Expr::Num(5.0)),Box::new(Expr::Num(5.0)),)),);println!("表达式: {}",describe(&div_zero));println!("结果: {:?}",eval(&div_zero));}

运行输出

plaintext

表达式: ((3 + 4) * 2)
结果: Some(14.0)
表达式: (10 / (5 - 5))
结果: None

重点知识点拆解

  1. 递归枚举 + Box
Add(Box<Expr>,Box<Expr>)

Expr 递归包含自身,Rust 无法在栈上直接存放无限大小的递归类型,必须用 Box(堆指针)。
Box :把 T 放到堆,栈上只存一个指针,解决递归类型大小不确定问题。

  1. ? 运算符(问号)
letva=eval(a)?;eval(a)返回Option<f64>
  • 如果是 Some(v) → 把 v 赋值给 va;
  • 如果是 None → 整个 eval 函数立刻提前返回 None。
    ? 是错误传播语法糖,等价于:
letva=matcheval(a){Some(v)=>v,None=>returnNone,};

⚠️ ? 只能用在返回 Option / Result 的函数内部,main 里面不能直接写。

  1. describe 递归打印

每一个二元运算符都包一层括号,不管运算符优先级,简单粗暴,保证结构清晰:

Add(Num(3),Num(4))(3+4)

拓展思考(进阶)

当前方案不做运算符优先级,所以 (3 + 4) * 2 输出 ((3 + 4) * 2) ,是正确的;
但如果表达式是 Add(Mul(Num(3),Num(4)),Num(5)) ,现在输出 ((3 * 4) + 5) ,
数学上其实可以省去外层括号 (3 * 4) + 5 。
想实现智能括号(根据优先级决定要不要加括号),就是一道经典的中级Rust练习题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 12:39:19

GLM-5.3-Flash 接入 Cline 实战:高性价比 AI 编程环境搭建指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 12:38:52

高校教材编写不用愁!AI教材生成工具一键搞定20万字书稿

写论文其实是一件挺费劲的事&#xff0c;特别是在梳理教材内容的时候。要把知识点理清楚&#xff0c;说清楚&#xff0c;关键是得做到趣味和深度的平衡。比如&#xff0c;小学教材如果写得太复杂&#xff0c;学生根本跟不上&#xff1b;高中教材要是太简单&#xff0c;又没什么…

作者头像 李华
网站建设 2026/9/6 12:35:17

AI图像生成工具部署指南:从Stable Diffusion到小马形态项目实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 12:33:11

java项目-第183期抗疫医疗用品销售系统-java毕业设计

java项目-第183期抗疫医疗用品销售系统-java毕业设计 今天分享的项目是《抗疫医疗用品销售系统》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以在前台查看抗疫物品、抗疫贴士、新闻资讯、跳转到个人后台 管理员负责登录后台系统&#xff0c;负责整个后台信息维护。功…

作者头像 李华