第九节、枚举
枚举
定义:
enum IpAddrKind {
V4,
V6,
}
枚举的值
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
作为函数的参数类型,定义如下:
fn route(ip_kind: IpAddrKind) {
....
}
route(IpAddrKind::V4);
route(IpAddrKind::V6);
结构体里使用枚举
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
字符串类型的枚举
可以直接将数据放在相同类型的使用相同枚举中,而不是使用结构体。代码如下:
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
枚举还可以使用不同类型的字段,代码如下:
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
还可以个更复杂的,把结构体放到枚举里:
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
还可以镶嵌多种类型:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
这个枚举有四种不同类型的变体:
- Quit根本没有与之关联的数据。
- Move像结构一样命名字段。
- Write包括一个String.
- ChangeColor包括三个i32值。
枚举和impl实现块
impl Message {
fn call(&self) {
// method body would be defined here
}
}
let m = Message::Write(String::from("hello"));
m.call();
Option
Option是Rust标准库中一个非常常见和有用的枚举,Option的价值是实现了一个Null,很多编程语言都需要有Null的表达。Rust就用Option来实现了它。在标准库里定义如下:
enum Option<T> {
None,
Some(T),
}
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
枚举+match做流控
这个操作类似switch...case。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
双层枚举做流控
#[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
...
value_in_cents(Coin::Quarter(UsState::Alaska)) 使用方法
...
搭配Option
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
在做match的时候,每一项都不能少,下面的代码会报错:
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
}
}
通过match 模式匹配 ,实现一个掷骰子的游戏:
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
这里要说的是other其他的,other是值match里面值之外的,这里就是3,7之外。 要是在other后面加一个5,
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other),
5 => 5,
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => anything(),
5 => 5,
}
这个代码,除了3,7之外都会走"", 那么3,7会不会走""呢?答案是不会,因为match是按照顺序执行的。那么5呢?5不会被执行,因为走了"_",这一点和"other"是有区别的。
简洁的if let流控
let config_max = Some(3u8);
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("The maximum is configured to be {}", max);
}
再看一个例子:
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
_ => count += 1,
}
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}