6.1 定义枚举6.1.1. 什么是枚举枚举允许我们通过列举所有可能的值来定义一个类型。这与其他编程语言中的枚举类似但 Rust 的枚举更加灵活和强大因为它们可以关联数据和方法类似于其他语言中的类或结构体。6.1.2. 定义枚举举个例子IP地址只有两种可能——IPv4和IPv6。它要么是IPv4要么是IPv6所以非常适合用枚举因为枚举的值只能是其所有变体枚举所有可能的值中的一个。enum IpAddrKind{ V4, V6, }这段代码使用enum关键字声明了一个名为IpAddrKind的枚举类型它有两个变体——V4和V6——分别代表IPv4和IPv6。6.1.3. 枚举值创建枚举值非常简单格式为枚举名::变体。例如let four IpAddrKind::V4; let six IpAddrKind::V6;枚举的变体位于该枚举标识符的命名空间下而这个标识符就是枚举类型的名字。我们可以声明一个接收IpAddrKind作为参数的函数传入的值既可以是V4也可以是V6fn route(ip_addr: IpAddrKind) { match ip_addr { IpAddrKind::V4 println!(IPv4), IpAddrKind::V6 println!(IPv6), } }让我们试试效果完整代码enum IpAddrKind{ V4, V6, } fn main() { let four IpAddrKind::V4; let six IpAddrKind::V6; // 调用函数 route(four); route(six); route(IpAddrKind::V4); } fn route(ip_addr: IpAddrKind) { match ip_addr { IpAddrKind::V4 println!(IPv4), IpAddrKind::V6 println!(IPv6), } }输出:IPv4 IPv6 IPv46.1.4. 将数据附加到枚举变体中枚举是一种自定义数据类型所以可以用作结构体字段的类型例如struct IpAddr { kind: IpAddrKind, address: String, }IpAddr中的kind字段类型是IpAddrKind用于存储网络协议另一个字段address是String类型用于存储具体的IP地址。通过这样的结构体我们可以在main()函数中声明一些存储IPv4、IPv6信息的变量:fn main() { let home IpAddr { kind: IpAddrKind::V4, address: String::from(127.0.0.1), }; let loopback IpAddr { kind: IpAddrKind::V6, address: String::from(::1), }; }Rust允许把数据直接附加到枚举变体上例如enum IpAddr { V4(String), V6(String), }在每个变体后面加上一个类型不必是同一种类型。这里V4和V6后面都跟了String类型。这种做法的优点是- 不需要额外使用结构体- 每个变体可以有不同的类型以及不同数量的关联数据例如enum IpAddr { V4(u8, u8, u8, u8), V6(String), }IPv4实际上由四个8位数字组成也就是四个能装进u8的值而IPv6是字符串所以应使用String。如果我们想把V4地址存成四个u8值同时仍把V6地址表示成String就无法使用结构体。枚举可以轻松处理这种情况。我们来重写一下前面的代码enum IpAddrKind{ V4(u8, u8, u8, u8), V6(String), } fn main() { let home IpAddrKind::V4(127, 0, 0, 1); let loopback IpAddrKind::V6(String::from(::1)); }确实比前面的代码短多了。6.1.5. 标准库中的IpAddr事实上标准库已经提供了表示IP地址的枚举。来看一下官方是怎么写的struct Ipv4Addr { // --snip-- } struct Ipv6Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), }Ipv4Addr和Ipv6Addr的内容这里没有写出来但这不是重点。重点是此代码说明任何类型的数据都可以放进枚举变体中例如字符串、数字类型或结构体。甚至还可以包含另一个枚举。6.1.6. 在枚举上使用方法Method方法Method的概念在上一篇文章 5.3. struct的方法(Method) 中已经介绍过这里不再过多展开。定义方法使用impl关键字如下例enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } impl Message { fn call(self) { println!(Something happens); } } fn main(){ let m Message::Write(String::from(hello)); m.call(); }该枚举有四种不同的变体-Quit不携带任何数据。-Move包含一个匿名结构体。-Write包含一个String。-ChangeColor包含三个i32值。在main中变量m被声明为Message枚举的Write变体并附带了String值hello。然后在m上调用call方法就会打印Something happens。