首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >07-Rust 教程 - 枚举与模式匹配

07-Rust 教程 - 枚举与模式匹配

作者头像
LarryLan
发布2026-04-13 14:52:49
发布2026-04-13 14:52:49
660
举报

枚举与模式匹配

枚举:一个变量的"多重人格",match 来拆分


🎬 引入

假设你要表示一个"消息"类型,它可能是:

  • 文本消息(带内容)
  • 图片消息(带 URL)
  • 语音消息(带时长)
  • 视频消息(带 URL 和时长)

在其他语言里,你可能得这样写:

代码语言:javascript
复制
// JavaScript... 自求多福
let message = {
    type: "text",
    content: "Hello",
    // 或者
    type: "image",
    url: "xxx.jpg",
    // 或者... 乱成一锅粥
};

但在 Rust 里,用**枚举(Enum)**就能优雅地表示:

代码语言:javascript
复制
enum Message {
    Text(String),
    Image(String),
    Voice(u32),
    Video(String, u32),
}

每个变体可以有不同的数据!而且编译器会确保你处理所有情况

今天咱们就来聊聊 Rust 的枚举和模式匹配,这可是 Rust 的杀手锏之一!


📌 核心概念

枚举是什么?

枚举(Enum)是自定义类型,它有多种可能的"变体"(Variant)。

生活化类比

  • 枚举 = 选择题的选项
  • 变体 = A/B/C/D 每个选项
  • 实例 = 你实际选的那个答案

定义枚举:enum 关键字

代码语言:javascript
复制
enum Direction {
    North,
    South,
    East,
    West,
}

最简单的枚举,每个变体不带数据。

代码语言:javascript
复制
enum Message {
    Quit,                       // 无数据
    Move { x: i32, y: i32 },   // 命名字段
    Write(String),              // 单个值
    ChangeColor(i32, i32, i32), // 多个值
}

重点:每个变体可以有不同的数据结构

实例化枚举

代码语言:javascript
复制
let m1 = Message::Quit;
let m2 = Message::Move { x: , y:  };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(, , );

注意:用 EnumName::Variant 语法。

match:模式匹配的核心

match 让你根据枚举的变体执行不同代码

代码语言:javascript
复制
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => ,
        Coin::Nickel => ,
        Coin::Dime => ,
        Coin::Quarter => ,
    }
}

重点match表达式,有返回值!而且必须穷尽所有可能

带数据的变体:提取数据

代码语言:javascript
复制
enum Message {
    Quit,
    Write(String),
}

fn handle_message(msg: Message) {
    match msg {
        Message::Quit => println!("退出"),
        Message::Write(text) => println!("文本:{}", text),
    }
}

text 是从 Write 变体里"提取"出来的。这叫模式匹配解构

if let:简化版 match

如果你只关心一个变体,其他情况不用处理,可以用 if let

代码语言:javascript
复制
let msg = Message::Write(String::from("Hello"));

// 完整 match
match msg {
    Message::Write(text) => println!("{}", text),
    _ => (),  // 其他情况啥也不干
}

// 简化 if let
if let Message::Write(text) = msg {
    println!("{}", text);
}

Option 枚举:Rust 的"空值"处理

Rust 没有 null!它用 Option 枚举表示"可能有值,也可能没有":

代码语言:javascript
复制
enum Option<T> {
    Some(T),
    None,
}

用法

代码语言:javascript
复制
let some_number: Option<i32> = Some();
let no_number: Option<i32> = None;

// 处理 Option
match some_number {
    Some(n) => println!("数字是:{}", n),
    None => println!("没有数字"),
}

为什么好? 编译器会强制你处理 None 情况,不会有空指针异常!

模式基础:不只是枚举

模式匹配可以用于:

  • 枚举变体
  • 字面量
  • 变量
  • 通配符 _
  • 结构体解构
代码语言:javascript
复制
let point = Point { x: , y:  };

match point {
    Point { x, y:  } => println!("在 X 轴上,x={}", x),
    Point { x: , y } => println!("在 Y 轴上,y={}", y),
    Point { x, y } => println!("x={}, y={}", x, y),
}

💻 代码示例

基础示例:完整的枚举处理

代码语言:javascript
复制
#[derive(Debug)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

fn can_pass(light: TrafficLight) -> bool {
    match light {
        TrafficLight::Red => false,
        TrafficLight::Yellow => false,
        TrafficLight::Green => true,
    }
}

fn action(light: TrafficLight) -> &str {
    match light {
        TrafficLight::Red => "停车",
        TrafficLight::Yellow => "等待",
        TrafficLight::Green => "通行",
    }
}

fn main() {
    let light = TrafficLight::Green;
    println!("可以通行吗?{}", can_pass(light));
    println!("动作:{}", action(light));
}

错误示例 1:没有穷尽所有变体

代码语言:javascript
复制
enum Color {
    Red,
    Green,
    Blue,
}

fn describe(color: Color) -> &str {
    match color {
        Color::Red => "红色",
        Color::Green => "绿色",
        // 忘记 Blue
    }
}

编译器错误

代码语言:javascript
复制
error[E0004]: non-exhaustive patterns: `Color::Blue` not covered
 --> src/main.rs:5:11
  |
5 |     match color {
  |           ^^^^^ pattern `Color::Blue` not covered
  |
  = note: `Color` defined as enum with 3 variants
help: ensure that all possible cases are being handled
  |
5 ~     match color {
6 |         Color::Red => "红色",
7 |         Color::Green => "绿色",
8 ~         Color::Blue => todo!(),
9 ~     }
  |

人话翻译:编译器:"Blue 怎么办?你想让我猜吗?把所有情况都处理了!"

修复:加上 Blue,或者用 _ 通配符。

代码语言:javascript
复制
match color {
    Color::Red => "红色",
    Color::Green => "绿色",
    Color::Blue => "蓝色",
}

// 或者
match color {
    Color::Red => "红色",
    _ => "其他颜色",
}

错误示例 2:类型不匹配

代码语言:javascript
复制
enum Message {
    Text(String),
    Number(i32),
}

fn handle(msg: Message) {
    match msg {
        Message::Text(n) => println!("{}", n),  // n 是 String,不是 i32
        Message::Number(s) => println!("{}", s), // s 是 i32,不是 String
    }
}

编译器错误

代码语言:javascript
复制
error[E0277]: `String` doesn't implement `std::fmt::Display`
 --> src/main.rs:7:38
  |
7 |         Message::Text(n) => println!("{}", n),
  |                                      ^^ `String` cannot be formatted

人话翻译:编译器:"你这里的变量类型搞反了!Text 里是 StringNumber 里是 i32,别乱来!"

错误示例 3:Option 不处理 None

代码语言:javascript
复制
fn get_first(vec: Vec<i32>) -> i32 {
    match vec.first() {
        Some(n) => *n,
        // 忘记 None
    }
}

编译器错误

代码语言:javascript
复制
error[E0004]: non-exhaustive patterns: `None` not covered
 --> src/main.rs:3:11
  |
3 |     match vec.first() {
  |           ^^^^^^^^^^^ pattern `None` not covered

人话翻译:编译器:"空数组怎么办?返回啥?想清楚!"

修复

代码语言:javascript
复制
fn get_first(vec: Vec<i32>) -> Option<i32> {
    match vec.first() {
        Some(n) => Some(*n),
        None => None,
    }
}

🐛 常见坑点

坑点 1:忘记 _ 通配符

当你不关心某些变体时,用 _

代码语言:javascript
复制
match msg {
    Message::Text(t) => println!("{}", t),
    _ => (),  // 其他情况啥也不干
}

坑点 2:Option 直接解包

代码语言:javascript
复制
let opt: Option<i32> = Some();
let n = opt.unwrap();  // 如果是 None,会 panic!

推荐做法

代码语言:javascript
复制
// 用 match
match opt {
    Some(n) => println!("{}", n),
    None => println!("没有值"),
}

// 用 if let
if let Some(n) = opt {
    println!("{}", n);
}

// 用 unwrap_or
let n = opt.unwrap_or();  // None 时返回 0

坑点 3:模式匹配后还想用原变量

代码语言:javascript
复制
let msg = Message::Text(String::from("Hello"));

match msg {
    Message::Text(text) => println!("{}", text),
    _ => (),
}

println!("{:?}", msg);  // 错误:msg 已被移动

修复:用借用。

代码语言:javascript
复制
match &msg {
    Message::Text(text) => println!("{}", text),
    _ => (),
}

坑点 4:if let 滥用

if let 只适用于只关心一个变体的情况。如果要处理多个,还是用 match


🎯 实战案例

案例 1:状态机

代码语言:javascript
复制
#[derive(Debug, PartialEq)]
enum OrderState {
    Pending,
    Processing,
    Shipped,
    Delivered,
    Cancelled,
}

impl OrderState {
    fn next(self) -> Option<Self> {
        match self {
            OrderState::Pending => Some(OrderState::Processing),
            OrderState::Processing => Some(OrderState::Shipped),
            OrderState::Shipped => Some(OrderState::Delivered),
            OrderState::Delivered | OrderState::Cancelled => None,
        }
    }
    
    fn can_cancel(&self) -> bool {
        match self {
            OrderState::Pending | OrderState::Processing => true,
            _ => false,
        }
    }
}

fn main() {
    let mut state = OrderState::Pending;
    println!("初始状态:{:?}", state);
    println!("可以取消吗?{}", state.can_cancel());
    
    state = state.next().unwrap();
    println!("下一状态:{:?}", state);
}

案例 2:用 Option 处理查找

代码语言:javascript
复制
fn find_user(users: &[(u32, &str)], id: u32) -> Option<&str> {
    for &(uid, name) in users {
        if uid == id {
            return Some(name);
        }
    }
    None
}

fn main() {
    let users = [
        (, "Alice"),
        (, "Bob"),
        (, "Charlie"),
    ];
    
    match find_user(&users, ) {
        Some(name) => println!("找到用户:{}", name),
        None => println!("用户不存在"),
    }
}

案例 3:嵌套模式匹配

代码语言:javascript
复制
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(State),
}

enum State {
    Alabama,
    Alaska,
    Arizona,
}

fn value(coin: &Coin) -> u32 {
    match coin {
        Coin::Penny => ,
        Coin::Nickel => ,
        Coin::Dime => ,
        Coin::Quarter(state) => match state {
            State::Alabama => ,
            State::Alaska => ,
            State::Arizona => ,
        },
    }
}

// 或者用守卫
fn describe(coin: &Coin) -> &str {
    match coin {
        Coin::Penny => "便士",
        Coin::Nickel => "镍币",
        Coin::Dime => "角币",
        Coin::Quarter(State::Alabama) => "阿拉巴马州 25 分币",
        Coin::Quarter(_) => "其他州 25 分币",
    }
}

🧠 思维导图

07-枚举与模式匹配
07-枚举与模式匹配

📝 小结

  1. 枚举变体可以带数据,每个变体结构可以不同
  2. match 必须穷尽所有可能,编译器会帮你检查
  3. Option<T> 替代 nullSome(T) 有值,None 无值
  4. if let 是简化版 match,只关心一个变体时用
  5. 模式匹配可以解构,直接提取变体里的数据

下篇预告:枚举里存数据,那数据存在哪?直接复制还是借用?下一篇聊聊借用与引用,彻底搞懂 Rust 的内存魔法!


🔗 参考资料

  • Rust Book - 枚举
  • Rust By Example - 枚举
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 枚举与模式匹配
    • 🎬 引入
    • 📌 核心概念
      • 枚举是什么?
      • 定义枚举:enum 关键字
      • 实例化枚举
      • match:模式匹配的核心
      • 带数据的变体:提取数据
      • if let:简化版 match
      • Option 枚举:Rust 的"空值"处理
      • 模式基础:不只是枚举
    • 💻 代码示例
      • 基础示例:完整的枚举处理
      • 错误示例 1:没有穷尽所有变体
      • 错误示例 2:类型不匹配
      • 错误示例 3:Option 不处理 None
    • 🐛 常见坑点
      • 坑点 1:忘记 _ 通配符
      • 坑点 2:Option 直接解包
      • 坑点 3:模式匹配后还想用原变量
      • 坑点 4:if let 滥用
    • 🎯 实战案例
      • 案例 1:状态机
      • 案例 2:用 Option 处理查找
      • 案例 3:嵌套模式匹配
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档