被低估的编程语言特性:模式匹配

当分支同时判断类型、字段和数据结构时,if/elif 很快就会变得难读。

模式匹配常被理解成“高级版 switch”,但它真正有用的地方,不是少写几行代码,而是把分支判断写得更贴近数据本身。Python 的 match、Rust 的 match,以及 Scala、Elixir、Haskell 中的同类机制,都在解决同一类问题:直接描述输入的形状,再决定如何处理。

它解决的不是简写,而是结构表达

普通 if/elif 的问题,不只是啰嗦,而是判断条件和数据结构经常分离。

例如拿到一段输入,它可能是字符串、元组、字典,或者某种固定形状的数据。if/elif 往往会变成这样:

1
2
3
4
5
6
7
8
9
10
def handle(msg):
if isinstance(msg, dict):
if msg.get("type") == "ping":
return "pong"
elif msg.get("type") == "log" and "content" in msg:
return msg["content"]
elif isinstance(msg, tuple):
if len(msg) == 2 and msg[0] == "add":
return msg[1] + 1
return None

这段代码可以工作,但阅读时需要先在脑中还原“数据应该长什么样”。

模式匹配把顺序反过来:先写出输入应有的结构,再写处理逻辑。

1
2
3
4
5
6
7
8
9
10
def handle(msg):
match msg:
case {"type": "ping"}:
return "pong"
case {"type": "log", "content": content}:
return content
case ("add", n):
return n + 1
case _:
return None

这里的重点不是语法新不新,而是代码把“接受什么输入”直接写出来了。

它不只是分支语句

把模式匹配等同于 switch,通常会低估它的价值。

switch 多数情况下只比较一个值;模式匹配可以同时判断类型、结构、位置和部分内容。这在下面几类场景里尤其明显。

1. 处理结构化输入

命令行参数、配置文件、JSON 响应、消息队列事件,本质上都不是单个值,而是一组有结构的数据。

如果一段逻辑同时在做这三件事:

  • 判断类型
  • 判断字段是否存在
  • 取出字段继续处理

那它通常适合尝试模式匹配。

2. 表达合法输入的边界

很多问题不是计算错误,而是未预期的输入混入了处理流程。

模式匹配的一个直接价值是:不合法的结构可以被明确排除,而不是依赖散落的 get()len() 和防御性判断。代码会更像一张白名单,只处理明确支持的输入形式。

这样做也更利于维护。后续阅读代码时,不需要再猜“默认支持哪些格式”。

3. 编写状态机

状态机的核心是:同一个输入,在不同状态下有不同处理方式。

当状态和事件需要组合判断时,模式匹配通常比多层 if 更清楚,因为它能把“当前状态 + 新输入”放在同一处匹配,而不是拆成两层条件。

它也不是银弹

并不是所有分支都适合改成模式匹配。

1. 条件是连续区间

例如“分数大于 90”“重试次数小于 3”。这类判断更接近数值条件,if 通常更直接。

2. 分支内部逻辑很重

如果每个 case 下面都有十几行业务代码,模式匹配只是把入口写整齐,复杂度并没有消失。更合适的做法是:匹配负责分流,具体处理拆到函数中。

3. 运行环境不统一

以 Python 为例,match 从 3.10 才开始支持。如果项目运行环境没有统一,使用这个特性可能会增加兼容成本。

怎么判断值不值得引入

可以直接看代码是否已经出现这些信号:

  • if/elif 超过 5 个分支
  • 同一批字段被反复 get()
  • 经常写 isinstance + len + 下标
  • 新增一种输入格式时,总要改很多地方

如果符合其中两条,模式匹配往往就不只是语法偏好,而是在降低分支复杂度。

与其把它看成一种“更高级的判断语句”,不如把它看成一种描述数据形状的方式。这样再回头看代码时,关注点会从“怎么判断”变成“允许什么输入”。


被低估的编程语言特性:模式匹配
https://ghost.kasumi.live/2026/04/28/被低估的编程语言特性:模式匹配/
作者
Amadeus
发布于
2026年4月28日
许可协议