声明枚举
enum 声明一种类型,它的值是一组固定的具名变体:
enum Status {
ready,
failed,
}
现在 Status 是一个恰好有两个值的类型:Status.ready 和 Status.failed。其他任何东西都不可能是 Status。
语法刻意保持得非常简单:
enum Name {开始声明。- 每行列出一个变体名,用逗号分隔。
}结束声明。
没有附带数据,没有判别值,也没有自动生成的方法:正因如此,enum 才是 Zero 中“最小的和类型”。
使用枚举
用枚举类型限定变体名来引用它:
let state: Status = Status.ready
当右侧已经确定了类型时,: Status 标注是可选的;大多数情况下你可以写:
let state = Status.ready
编译器会推断出类型是 Status。
比较枚举值
两个枚举值是同一个变体时,它们就相等:
if state == Status.ready {
check world.out.write("ready\n")
} else {
check world.out.write("not ready\n")
}
这是对枚举做分支最简单的方式。如果要做穷尽分析,显式处理每一个变体,就用 match:
match state {
.ready => { check world.out.write("ready\n") }
.failed => { check world.out.write("failed\n") }
}
当你以后添加第三个变体时,match 相对于 if/else if 的优势就显现出来了。编译器会指出每一个缺少新情况的 match;而 if/else 链会悄悄落入默认分支。
Choice 和 match 会更详细地介绍 match。它对 enum 和 choice 都适用。
一个完整示例
Zero 的官方示例把 enum 和 choice 放在同一个文件里对照展示:
在这段代码中,Status 什么也没做,它只是用来做对比。choice 的变体在匹配时会绑定数据(value、message);enum 的变体什么都不绑定,因为没有可绑定的东西。
Enum 还是 Choice:快速决策
一条简单的规则:
- 变体只是标签 →
enum。 - 变体需要携带数据 →
choice。
如果你在建模生命周期状态,后来需要给“failed”状态附加一条错误信息,就把类型从 enum 改成 choice。每个变体都会有自己的数据类型,下游的 match 分支也会多出一个绑定这份数据的名字。编译器会一步步引导你完成这次重构。
具体来说:
// Before - enum, no payloads
enum Status {
ready,
failed,
}
// After - choice with payloads on each variant
choice Status {
ready: Void,
failed: String,
}
数据类型为 Void 的变体,在 choice 形式中就只是标签。对于同一组逻辑状态,你既可以用 enum,也可以用 choice;当你确实不需要附加数据时,就选 enum。
使用场景
一些日常中适合用 enum 的例子:
- 不带元数据的生命周期。
Loading、Ready、Empty:纯粹的状态,不带数据。 - 模式。 文件打开模式
Read、Write、Append。 - 方向。
North、South、East、West。 - 日志级别。
Trace、Debug、Info、Warn、Error。(以后你可能想加上一条消息,到那时就该换成choice。) - 星期几。 一个经典例子。
凡是你原本想用魔法整数常量的地方(0 = 待处理,1 = 进行中,2 = 已完成),用 enum 几乎总是更清楚。
风格建议
- 变体名使用小写,与 Zero 中其他标识符的风格一致。
- 最后一个变体后面加上逗号是可以的(也推荐这样做,方便查看 diff:添加新变体时不会改动上一行)。
- 让枚举列表保持简短。如果你有十几个变体,而且其中很多都想带数据,那你需要的可能是
choice,或者重新设计,而不是一个更大的enum。
下一步:Choice 和 Match
接下来自然要看它更强大的近亲:choice 和 match,也就是 Zero 的带标签联合类型,以及与之配套的模式匹配结构。
常见问题
Zero 中的 enum 是什么?
enum 声明一种类型,它的值只能是一组固定具名变体中的一个,这些变体只是标签,不带任何额外数据。示例:enum Status { ready, failed }。Status 类型的值只能是 Status.ready 或 Status.failed 之一,编译器会强制保证这一点。
enum 和 choice 有什么区别?
enum 的变体是纯标签,不携带数据。choice 是带标签的联合,每个变体都有一个关联的数据类型,比如 choice Result { ok: i32, err: String }。只需要按名字区分情况时用 enum;每种情况都要携带额外信息时用 choice。
怎么判断一个值是哪个 enum 变体?
把值和变体比较:if status == Status.ready { ... }。如果要对所有变体做穷尽的分支,就用 match:漏掉某个变体时编译器会发出警告,这也是当值属于和类型时,优先用 match 而不是 if/else if 链的主要原因。
Zero 的 enum 变体可以带关联值吗?
不可以,这正是 choice 的用途。enum 被有意设计成最精简的和类型:每个变体只是一个标签。如果你需要给某个变体附加一个 i32 或 String,说明 enum 已经不够用了,你需要的是 choice。
Zero 中什么时候该用 enum?
当一个值必须是一小组具名状态中的某一个,而且这些状态不携带额外数据时,就用 enum。例如:星期几、红绿灯颜色、不带元数据的生命周期状态、日志级别。如果你发现自己想给某个变体附加数据,就换成 choice。