从“漏写 else-if”到“编译器兜底”:看 Rust 的 match 如何彻底干掉“漏分支”型 Bug

50 阅读2分钟

一个真实又眼熟的“坑”

在业务开发中,条件分支遗漏是一种高频且危害显著的缺陷模式。以下段 JavaScript 代码为例:

if (res === 200) {
  console.log(200);
} else if (res === 300) {
  console.log(300);     
} else if (res === 400) {
  console.log(400);
}
// ……后面没有 else 了

当接口返回 500、502 等新状态码时,流程会悄无声息地落入“无处理”路径,导致行为未定义。这类缺陷往往在测试阶段难以穷尽,最终触发线上故障。

Rust 的解决方案:穷尽性模式匹配,不写全就编译失败,没有“商量”二字

Rust 提供了 match 表达式,并通过编译期的穷尽性(exhaustiveness)检查,强制开发者覆盖所有可能情况:

enum HttpCode {
   Ok,          // 200
   Redirect,    // 300
   BadRequest,  // 400
}

fn handle(res: HttpCode) {
   match res {
       HttpCode::Ok => println!("200"),
       HttpCode::Redirect => println!("3000"),
       HttpCode::BadRequest => println!("4000"),
       // 若后续新增 HttpCode::ServerError,此处将编译失败:
       // error[E0004]: non-exhaustive patterns: `ServerError` not covered
   }
}

编译器在代码生成前即可发现“遗漏分支”,把运行时风险转化为编译期错误,显著降低缺陷逃逸概率。

翻译成人话: “想偷懒?可以,先把编译器打服。”

在 Rust 的世界里, “漏分支”不是风格问题,是门都进不了的硬错误

别的语言靠 Code Review 劝你善良;Rust 直接物理封印,不写全就寸步难行

通配符?可以,但必须显式找骂

match code {
   200 => {},
   300 => {},
   400 => {},
   _ => { log::warn!("未知码,自行翻车"); },
}

你把 _ 通配符删掉试试?编译器立刻原地爆炸给你看。

落地建议:在现有工程中借力 Rust 思维

  1. TypeScript 代码引入穷尽性检查

    利用 never 类型与联合类型,模拟 Rust 的穷尽性:

    type Code = 200 | 300 | 400 | 500;
    function handle(res: Code) {
      if (res === 200) { /* ... */ }
      else if (res === 300) { /* ... */ }
      else if (res === 400) { /* ... */ }
      else if (res === 500) { /* ... */ }
      else {
        const _exhaustive: never = res; // 新增枚举值时此处报错
      }
    }
    

    需注意:必须开启 strictNullChecks 等严格选项,并确保团队成员持续遵守。

  2. Code Review 聚焦“可枚举类型”

    将“条件分支是否完整”设为 PR 必检项,与自动化测试、静态分析结合,形成多层防护。