首页
/ Rustlings enums 章节精讲:用枚举变体与模式匹配表达代数数据类型

Rustlings enums 章节精讲:用枚举变体与模式匹配表达代数数据类型

2026-09-04 16:58:34作者:滕妙奇

Rust 的枚举(enum)是一种"枚举出所有可能取值"的类型定义机制,Rustlings 的 enums 章节将其定位为与函数式语言(F#、OCaml、Haskell)中代数数据类型(ADT)相当的核心特性,并强调其与模式匹配(pattern matching)配合使用的价值。本篇围绕 exercises/08_enums 下的三道递进式练习(enums1、enums2、enums3)展开:从定义最简单的单元变体,到为变体携带数据(结构体式、元组式两种形式),再到用 match 对枚举进行解构分发,最后结合 Rustlings 工具链的 run / hint 机制说明如何在本仓库中实际完成并验证这一章节。

为什么 Rust 的枚举与其他语言不同

章节 README 开篇即指出:枚举是许多语言都具备的特性,但各语言的枚举能力参差不齐,Rust 的枚举最接近函数式语言的代数数据类型。其关键差异在于:Rust 的每个变体(variant)可以携带不同的数据,并且编译器强制对枚举做穷尽处理。

对照 exercises/README.md 中的"练习到书章节映射表",enums 对应 The Rust Book 的 §6(枚举与模式匹配)与 §19.3(模式语法),这也解释了本章练习的组织方式——先认识变体形态,再落到模式匹配上。

Rustlings 仓库为每个练习同时维护了两份文件:待完成的练习文件(exercises/ 下,含 TODO 占位)与参考解答(solutions/ 下)。例如 enums1 的练习与解答分别是 exercises/08_enums/enums1.rssolutions/08_enums/enums1.rs,这种"题目 + 标准答案"的结构本身就是理解每个知识点的最佳路径。

enums1:单元变体与 #[derive(Debug)]

练习文件 enums1.rs 给出了骨架:

#[derive(Debug)]
enum Message {
    // TODO: Define a few types of messages as used below.
}

fn main() {
    println!("{:?}", Message::Resize);
    println!("{:?}", Message::Move);
    println!("{:?}", Message::Echo);
    println!("{:?}", Message::ChangeColor);
    println!("{:?}", Message::Quit);
}

main 函数用 {:?}(调试格式)打印了五个变体,因此需要补全的正是这五个不携带数据的单元变体标准解答 为:

#[derive(Debug)]
enum Message {
    Resize,
    Move,
    Echo,
    ChangeColor,
    Quit,
}

这里有两个值得注意的实现细节:

  1. #[derive(Debug)] 的必要性{:?} 格式化要求类型实现 Debug trait。单元变体枚举无法手动感知内容,derive 宏会在编译期为 Message 自动生成 Debug 实现,直接打印变体名(如 Resize)。若移除 deriveprintln!("{:?}", Message::Resize) 会因 Message 未实现 Debug 而编译失败。
  2. 单元变体(unit-like variant)的语义:这种形态等价于 C 的枚举常量,仅表示"某件事发生了",不携带任何状态。这也是为什么 info.toml 中该练习的 hint 只有一句 "No hints this time ;)"(见 rustlings-macros/info.toml 第 434 行附近)——它被设计成本章的热身。

用 Rustlings 运行与查看提示的命令(命令定义见 src/cli.rs 中的 Command 枚举):

rustlings run enums1     # 编译并运行该练习,通过后标记为 done
rustlings hint enums1    # 输出该练习的提示

enums2:让变体携带数据——三种变体形态

第二个练习 enums2.rs 要求变体携带真实数据。练习给出的构造表达式已经暗示了每个变体应有的形态:

#[derive(Debug)]
struct Point {
    x: u64,
    y: u64,
}

#[derive(Debug)]
enum Message {
    // TODO: Define the different variants used below.
}

impl Message {
    fn call(&self) {
        println!("{self:?}");
    }
}

fn main() {
    let messages = [
        Message::Resize { width: 10, height: 30 },
        Message::Move(Point { x: 10, y: 15 }),
        Message::Echo(String::from("hello world")),
        Message::ChangeColor(200, 255, 255),
        Message::Quit,
    ];

    for message in &messages {
        message.call();
    }
}

info.toml 中为 enums2 配置的官方 hint 明确给出了三种变体形态的命名与示例:

Enum variants can be defined using three different forms: struct-, tuple- and unit-like.

enum EnumUsingAllVariantForms {
    StructLike { named_field: bool },
    TupleLike(bool),
    UnitLike,
}

对照 main 中的构造方式,可以逐个确定每个变体的形态与字段类型:

变体 构造方式 形态 应定义的类型
Resize Resize { width: 10, height: 30 } 结构体式(struct-like) Resize { width: u64, height: u64 }
Move Move(Point { x: 10, y: 15 }) 元组式(tuple-like),携带一个 struct Point Move(Point)
Echo Echo(String::from("hello world")) 元组式,携带 String Echo(String)
ChangeColor ChangeColor(200, 255, 255) 元组式,携带三个无符号字节 ChangeColor(u8, u8, u8)
Quit Quit 单元式(unit-like) Quit

solutions/08_enums/enums2.rs 中的完整定义为:

#[derive(Debug)]
enum Message {
    Resize { width: u64, height: u64 },
    Move(Point),
    Echo(String),
    ChangeColor(u8, u8, u8),
    Quit,
}

几个类型的取值细节值得留意:

  • width / heightu64,与 Pointxy 字段类型一致(见练习文件中已给出的 struct Point 定义);
  • ChangeColor(u8, u8, u8) 三个分量对应 RGB 三原色,u8 的取值范围 0~255 正好覆盖标准颜色通道的量程——这正是元组式变体适合表达"一组同质数据"的场景;
  • 元组式变体的分量是匿名的,只能按位置取值;结构体式变体是具名字段,可按名字取值。二者的取舍通常取决于字段是否有自解释的语义(宽高用名字更清晰,RGB 三通道用位置足矣)。

此外,练习还展示了枚举上的方法实现:impl Message { fn call(&self) } 接收 &self,以引用方式访问枚举值并打印。main 中通过 for message in &messages 遍历切片借用,逐个调用 message.call()——这说明枚举值与结构体一样,是普通类型,可以放进数组、取引用、实现方法,只是"类型的取值空间"被显式枚举了出来。

运行验证时注意该练习在 info.toml 中标记为 test = false,即 rustlings run enums2 只要求编译并正常执行、输出五个变体的 Debug 表示,没有断言测试。

enums3:用 match 解构枚举,驱动状态机

第三个练习 enums3.rs 把枚举、方法与单元测试串起来:Message 的定义已给出(同 enums2 解答),核心任务是实现 State::process,把每种消息分发给对应的处理方法:

impl State {
    fn resize(&mut self, width: u64, height: u64) { ... }
    fn move_position(&mut self, point: Point) { ... }
    fn echo(&mut self, s: String) { ... }
    fn change_color(&mut self, red: u8, green: u8, blue: u8) { ... }
    fn quit(&mut self) { ... }

    fn process(&mut self, message: Message) {
        // TODO: Create a match expression to process the different message
        // variants using the methods defined above.
    }
}

官方 hint(info.toml)分两步给出指引:先定义枚举使其通过编译,再在 process() 中创建 match 表达式,并特别提醒"需要在 match 表达式中解构(deconstruct)某些消息变体以取出其值"。

solutions/08_enums/enums3.rsprocess 的实现即本章"模式匹配"主题的落点:

fn process(&mut self, message: Message) {
    match message {
        Message::Resize { width, height } => self.resize(width, height),
        Message::Move(point) => self.move_position(point),
        Message::Echo(string) => self.echo(string),
        Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),
        Message::Quit => self.quit(),
    }
}

这段代码体现了模式匹配解构变体的三种写法:

  1. 结构体式变体Message::Resize { width, height } 按字段名解构,widthheight 直接成为匹配臂里的绑定变量;
  2. 元组式变体Message::ChangeColor(red, green, blue) 按位置解构三个 u8 分量;Message::Move(point) 则是整体绑定 Point 值;
  3. 单元式变体Message::Quit 无需绑定,直接映射到 self.quit()

更关键的是 Rust 的穷尽性检查match 必须覆盖 Message 的全部五个变体,遗漏任何一个(例如忘了 Quit)都会产生编译错误而非静默忽略。从 solutions/08_enums/enums3.rsexercises/08_enums/enums3.rs 的对照可见,两者除 process 的实现和一处注释外完全一致——这正是 rustlings 的练习机制:练习文件删除关键代码段,解答文件补全后作为验收基准。

练习文件末尾的 #[cfg(test)] 测试模块则是"穷尽性"的直接验证器:

#[test]
fn test_match_message_call() {
    let mut state = State {
        width: 0,
        height: 0,
        position: Point { x: 0, y: 0 },
        message: String::from("hello world"),
        color: (0, 0, 0),
        quit: false,
    };

    state.process(Message::Resize { width: 10, height: 30 });
    state.process(Message::Move(Point { x: 10, y: 15 }));
    state.process(Message::Echo(String::from("Hello world!")));
    state.process(Message::ChangeColor(255, 0, 255));
    state.process(Message::Quit);

    assert_eq!(state.width, 10);
    assert_eq!(state.height, 30);
    assert_eq!(state.position.x, 10);
    assert_eq!(state.position.y, 15);
    assert_eq!(state.message, "Hello world!");
    assert_eq!(state.color, (255, 0, 255));
    assert!(state.quit);
}

测试依次向 State 投递五种消息,再逐一断言各字段是否按预期更新——width/height 变为 10/30、位置变为 (10, 15)、消息字符串被替换、颜色变为 (255, 0, 255)、quit 置为 true。由于该练习在 info.toml没有 test = false 标记,rustlings run enums3 会以 cargo test 方式执行此测试,全部断言通过才判定完成——这与 enums1/enums2 的"编译 + 运行"验收标准不同。

从源码结构看,这套"练习/解答双轨"机制由 dev/Cargo.toml 中的 target 清单驱动:每个练习注册为一对 target(如 enums1enums1_sol,分别指向 ../exercises/08_enums/enums1.rs../solutions/08_enums/enums1.rs),配合 rustlings-macros/info.toml 中的 namedirtesthint 元数据,由 rustlingsrun / hint / check-all 子命令(定义于 src/cli.rs)统一调度。想批量核验本章进度,可以执行 rustlings check-all 查看各练习的 done/pending 状态。

小结:本章练习的知识递进

练习 核心知识点 验收方式
enums1 单元变体、#[derive(Debug)]{:?} 打印 编译并运行(test = false
enums2 结构体式 / 元组式 / 单元式三种变体形态、携带数据的变体、枚举上的 impl 方法 编译并运行(test = false
enums3 match 穷尽匹配与变体解构、以枚举消息驱动状态更新 编译 + cargo test 断言

这一章的完整学习闭环是:先理解"Rust 枚举 = 代数数据类型"这一定位,再依次掌握"变体能携带什么数据"(三种形态)与"如何把数据取出来"(模式匹配解构),最后依靠编译器的穷尽性检查与单元测试(test_match_message_call)双重保证分发逻辑没有遗漏。完成 exercises/08_enums 全部三个练习后,读者即可具备编写"消息驱动状态机"这类典型 Rust 模式的基础,这也是后续 20_threads 等章节中多线程消息传递的常见写法。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384