Menu

Zero 枚举:不带数据的简单枚举类型

Zero 中的 enum 怎么用:声明一组固定的具名变体、比较值,以及 enum(纯标签)和 choice(带标签的联合)之间的界线。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

声明枚举

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。

Coddy 编程语言插图

和 Coddy 一起学编程

开始