多选一,但保留选择的身份

和类型是由若干可区分分支构成的类型:一个值选择其中一个分支,并携带该分支要求的数据。二元形式常写作 A+B,称为互斥和或不交并;它不是把两个数相加,也不是同时保存 A 和 B 的一对值。后者属于积类型。

“互斥”不要求 A 与 B 原先毫不相同,而是构造时为它们保留不同身份。即使两边都是布尔类型,“左边的真”与“右边的真”仍是不同值。若只数有限、正常终止的值,左边有 m 种、右边有 n 种,和类型便有 m+n 种,而积类型有 m×n 种。这是名称中“和”的含义,不是程序自动执行了算术。

参考:[1]

构造与消去相互配套

构造规则是把一个 A 值注入左分支,或把一个 B 值注入右分支。消去规则则是分类讨论:遇到左分支时按 A 使用里面的数据,遇到右分支时按 B 使用。简单类型演算要求两条处理分支产生同一个结果类型,整个分类表达式才能取得该类型。

只有内部数据不足以确定完整的和类型。例如“左边的整数 5”仍需知道右边允许什么。教材中的简单演算用类型标注补全这部分信息,实际语言也可结合上下文与类型推断。标签的职责不是装饰名字,而是让分支判断与内部数据类型保持对应,避免把错误分支的数据当成另一种类型读取。

参考:[1] [2]

用成功或错误表达一次结果

Rust 的 Result<T,E> 提供直观实例:Ok(T) 携带成功值,Err(E) 携带错误值。设一个读取数量的操作返回成功整数或错误说明,Ok(7) 与 Err(“格式错误”) 就分别代表两种结果。处理者匹配 Ok 时才能取得整数继续计算,匹配 Err 时则取得说明并选择报告或传播错误,而不是约定某个普通整数偷偷代表失败。

Rust 的 match 要求模式覆盖所有可能情形,遗漏错误分支而又没有兜底模式时,编译器会拒绝该匹配。兜底模式也能满足覆盖要求,但并不证明里面的业务处理正确。Result 的必须使用标记通常对忽略结果发出警告;它不是保证开发者一定认真恢复错误的证明。因此,类型能防止部分结构性混淆,却不能替代错误处理策略。

参考:[3] [4]

枚举、标签联合与裸联合

带数据的枚举可以直接表达多个带名称的分支;Rust enum 既允许无字段分支,也允许携带元组或具名字段。只有几个名字、不带附加数据的枚举,可以看成这种选择结构的简单情形。所谓标签联合强调判别信息与分支数据的对应,而未标记联合强调同一存储区域可以按不同字段解释,两者不能仅凭“联合”一词混同。

以 Rust union 为例,字段共享存储,读取需要程序员保证位模式对所读类型有效,并置于不安全代码中;它不会自动替程序判断当前应读取哪个字段。这也说明和类型不能与 C 的裸联合直接等同。另一方面,逻辑上有标签不代表内存里必须额外放一个固定大小的标签整数:Rust 允许编译器采用其他区分变体的布局。安全的选择语义与具体内存叠放方式是两个层次。

参考:[2] [4]