枚举是一个很多语言都有的功能,不过不同语言中其功能各不相同但是要表达的意思是一致的,枚举就是对于一个事物可以穷举出所有可能得值。比如说人的性别就可以用枚举,男人和女人两种。下面我们来学习Rust中的枚举。
一:枚举定义
定义一个IP地址的枚举,对于IP地址目前广泛使用的有两种,IPV4和IPV6。任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的,而且不能两者都是。IP 地址的这个特性使得枚举数据结构非常适合这个场景,因为枚举值只可能是其中一个成员。IPv4 和 IPv6 从根本上讲仍是 IP 地址,所以当代码在处理适用于任何类型的 IP 地址的场景时应该把它们当作相同的类型。
rust
enum IpAddrKind {
V4,
V6,
}
定义一个 IpAddrKind
枚举来表现这个概念并列出可能的 IP 地址类型,V4
和 V6
二:枚举值使用
创建 IpAddrKind
两个不同成员的实例:
rust
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
函数入参为枚举类型
rust
fn route(ip_type: IpAddrKind) { }
调用上面的route函数
rust
route(IpAddrKind::V4);
route(IpAddrKind::V6);
我们定义一个结构体,里面有个两个成员,一是指示IP类型,二是定义IP地址
rust
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
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"),
};
println!("home ip is {}",home.address);
println!("loopback ip is {}",loopback.address);
}
运行结果

有告警,因为结构体kind没有使用
我们可以使用一种更简洁的方式来表达相同的概念,仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。IpAddr
枚举的新定义表明了 V4
和 V6
成员都关联了 String
值:
rust
#[derive(Debug)]
enum IpAddr {
V4(String),
V6(String),
}
fn main() {
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
println!("home ip is {:?}",home);
println!("loopback ip is {:?}",loopback);
}
运行结果

用枚举替代结构体还有另一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将 V4
地址存储为四个 u8
值而 V6
地址仍然表现为一个 String
rust
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
再看一个稍微复杂的枚举例子,它的成员中内嵌了多种多样的类型
rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
这个枚举有四个含有不同类型的成员:
Quit
没有关联任何数据。Move
包含一个匿名结构体。Write
包含单独一个String
。ChangeColor
包含三个i32
。
枚举和结构体还有另一个相似点:就像可以使用 impl
来为结构体定义方法那样,也可以在枚举上定义方法。这是我们在 Message
枚举上定义了一个叫做 call
的方法:
rust
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();