Skip to content
📖 Auto 版 Rust Book本书目录

枚举与模式匹配 ​

在本章中,我们将介绍 枚举(enumerations),也称为 enums。枚举允许你通过列举其可能的变体来定义一个类型。首先,我们将定义和使用一个枚举,展示枚举如何将含义与数据一起编码。接下来,我们将探索一个特别有用的枚举 —— Option,它表示一个值可以是某个值,也可以什么都不是。然后,我们将了解如何使用 is 关键字进行模式匹配,从而轻松地为枚举的不同值运行不同的代码。最后,我们将介绍 Auto 的 ?? 运算符如何提供一种简洁的方式来处理可选值。

定义枚举 ​

结构体提供了一种将相关字段和数据组织在一起的方式(比如带有 width 和 height 的 Rectangle),而枚举则提供了一种表示某个值是一组可能值之一的方式。例如,我们可能想说 Rectangle 是一组可能形状中的一种,这组形状还包括 Circle 和 Triangle。为此,Auto 允许我们将这些可能性编码为枚举。

让我们看一个需要在代码中表达的场景,看看为什么枚举比结构体更合适。假设我们需要处理方向。我们可以 枚举 所有可能的变体 —— 这就是"枚举"名称的由来。

定义一个 `Direction` 枚举

Direction 现在是一个自定义数据类型,可以在代码的其他地方使用。注意与 Rust 的关键区别:

特性AutoRust
访问变体Direction.NorthDirection::North
显示输出${dir}(自动 Display)需要 #[derive(Display)]

Auto 使用点号(.)访问枚举变体,而 Rust 使用双冒号(::)。Auto 还会自动为枚举生成 Display 实现,允许你直接在字符串插值中使用它们。

枚举值 ​

我们可以创建每个变体的实例,并将它们传递给函数:

将枚举值传递给函数

带数据的枚举 ​

枚举相对于结构体的一个优势是,每个变体可以持有不同类型和数量的数据。我们不需要在结构体内部嵌套枚举,而是可以直接将数据放入每个枚举变体中:

一个 `IpAddr` 枚举,其变体存储 `String` 值

每个变体可以有不同类型和数量的关联数据。以下是空变体和带数据变体混合的例子:

一个 `Message` 枚举,不同变体持有不同数据

关键区别:

特性AutoRust
空变体Quit(无括号)Quit
带数据变体Write StringWrite(String)
构造带数据变体Message.Write("hello")Message::Write(String::from("hello"))

枚举方法 ​

与结构体一样,我们可以使用 ext 关键字为枚举定义方法:

使用 `ext` 在枚举上定义方法

ext 关键字对枚举的工作方式与结构体相同 —— 它添加方法(生成 Rust 的 impl 块),使用隐式 self 和 .field 简写。

Option 枚举 ​

Auto 的 Option 类型使用 ?T 语法表示,代表一个值可能是某个值,也可能什么都不是。这是 Auto 中等价于 Rust Option<T> 的写法:

auto
// ?T 是 May<T>(Auto 的 Option 类型)的语法糖
// 它可以是 Some(value) 或 None
rust
// Option<T> 可以是 Some(value) 或 None

?T 语法消除了 Tony Hoare 著名的"十亿美元错误"——空引用问题。Auto 不使用 null,而是让你显式处理值可能缺失的情况。

使用 `?T` 作为返回类型,配合 `Some` 和 `None`

Option 的关键区别:

特性AutoRust
Option 类型?TOption&lt;T&gt;
有值Some(value)Some(value)
无值NoneNone
函数返回fn foo() ?intfn foo() -&gt; Option&lt;i32&gt;

因为 ?T 和 T 是不同的类型,编译器不会让你把可选值当作确定存在的值来使用。你必须显式处理 None 的情况。

使用 is 进行模式匹配 ​

Auto 使用 is 关键字进行模式匹配,等价于 Rust 的 match 表达式。is 表达式将一个值与一系列模式进行比较,并根据匹配到的模式执行相应的代码。

基本模式匹配 ​

使用 `is` 对枚举进行模式匹配

关键区别:

特性AutoRust
匹配关键字is value { }match value { }
通配符分支else =&gt;_ =&gt;
分支分隔符换行(无逗号)分支之间用逗号
模式语法Variant.Name(binding)Variant::Name(binding)

绑定值的模式 ​

is 最强大的特性之一是模式可以绑定到枚举变体内部的值:

使用 `is` 解构枚举变体

当 atom 是 Atom.Int(11) 时,模式 Atom.Int(i) 匹配,i 绑定到值 11。绑定的变量随后可以在分支代码中使用。

使用 is 匹配 Option ​

我们可以像处理其他枚举一样,使用 is 来处理 ?T(Option)值:

使用 `is` 匹配 `?int`(Option)值

匹配是穷尽的 ​

与 Rust 的 match 一样,Auto 的 is 是穷尽的 —— 编译器会确保你处理了每一种可能的情况。如果你遗漏了某个情况,你将得到编译时错误。

使用 else 作为通配符 ​

else 分支是通配符模式,等价于 Rust 的 _:

使用 `else` 作为通配符模式

else 分支匹配所有未在其他分支中列出的值。注意 else 必须放在最后,因为分支是按顺序求值的。

使用 ?? 简洁处理可选值 ​

??(空值合并)运算符提供了一种简洁的方式来为可选值提供默认值:

`??` 空值合并运算符

?? 运算符是 Auto 中等价于 Rust .unwrap_or() 方法的写法。如果左侧有值,就返回该值;否则返回右侧的默认值。

当你想提供一个回退值而不需要使用完整的 is 匹配时,这非常有用:

auto
// 繁琐:完整的 is 匹配
let result = is optional_value {
    Some(v) => v
    None => default_value
}

// 简洁:?? 运算符
let result = optional_value ?? default_value

总结 ​

Auto 的枚举和模式匹配提供了与 Rust 相同的强大功能,但语法更加简洁:

概念AutoRust
定义枚举enum Name { ... }enum Name { ... }
访问变体Name.VariantName::Variant
数据变体Variant TypeVariant(Type)
模式匹配is value { }match value { }
通配符分支else =&gt;_ =&gt;
Option 类型?TOption&lt;T&gt;
有值Some(value)Some(value)
无值NoneNone
空值合并x ?? defaultx.unwrap_or(default)
枚举方法ext Name { }impl Name { }

枚举是 Auto 工具箱中一个强大的工具。结合第 5 章的结构体,你可以创建自定义类型来表达程序领域中的概念。is 模式匹配和 ?? 运算符使得处理枚举和可选值既安全又简洁。

接下来让我们进入第 7 章,了解 Auto 如何使用包和模块来组织代码。

基于 MIT 许可发布。