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也可以是V6:
fn 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。