关于「解析,而非校验」的 Rust 思考
来源: eli.thegreenplace.net — 2026-09-26
概述
Eli Bendersky 复盘 Alexis King 那篇经典文章「解析,而非校验」(Parse, don't validate)在 Rust 中的实践:与其在运行时校验一个可能为空的值、再让每一处调用都处理不可能发生的情况,不如让类型系统承载不变量。他依次考察了 NonEmpty、uutils 的 Pipeline、rust-analyzer 的 AbsPathBuf、camino 的 Utf8PathBuf、NonZero 以及 serde 反序列化这些真实例子,说明如何通过类型精化把校验前移,使后续代码不必再检查。
核心要点
- Vec::first 返回 Option<&T>,于是即便函数已保证列表非空,调用方仍被迫处理不可能发生的 None。
- 解法是引入 NonEmpty 这类「不可能为空」的类型,它的 first 直接返回引用,不变量在构造那一刻建立。
- 这正是「解析」与「校验」的区别:返回值从 Vec 换成 NonEmpty,数据被转换成了承载新含义的实体。
- uutils/coreutils 的 Pipeline 使用 NonEmpty<Command>,解析器只在确实解析出命令时才返回有效的管道,否则返回 None。
- rust-analyzer 的 AbsPathBuf 把「绝对路径」写进类型,配合 camino 的 Utf8PathBuf,构成 PathBuf → Utf8PathBuf → AbsPathBuf 的逐级精化。
- NonZero 不仅免去除零判断,还能用零位模式表示 None,使 Option<NonZeroUsize> 与 NonZeroUsize、usize 同尺寸。
- serde 的派生结构体让字段类型、枚举取值与非零整数在反序列化时一次校验完毕,之后无需再验;Python 与 JS 中这类校验通常要手写。
金句
核心问题在于,Vec 本质上就是一个可以为空的类型:你可以在相关代码上到处带上「这个不会为空,拉钩保证」的注释,但它不会被任何东西形式化地检查。
👍 0
👎 0
← 返回 Lobsters 首页