枚举和模式匹配
6.1. 定义枚举
枚举基础
枚举和结构类似,都有自己的变量和方法,不过枚举更倾向于列举出所有的可能性。
枚举可以作为数据类型
自己定义枚举 IP =
fn main() {
// 定义枚举
#[derive(PartialEq)] // to use "=="
enum IpAddrKind {
V4,
V6,
}
// 访问枚举
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
// 作为函数参数类型
fn route(ip_type: IpAddrKind) {
if ip_type == IpAddrKind::V4 {
println!("Type of route is V4");
} else {
println!("Type of route is V6");
}
}
route(four); // Type of route is V4
route(six); // Type of route is V6
}
枚举可以包含数据,关联方法(有点像结构体了)
fn main() {
// 枚举可以包含数据
#[derive(PartialEq)]
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
impl IpAddr {
fn test(&self) {
println!("我也不知道干点啥\n");
}
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V61");
home.test();
}
更好的方案,enum 里边套结构体
这个是rust 标准库里边对 IP 类型枚举的实现方案
#![allow(unused)]
fn main() {
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
}
Option 枚举和其相对于空值的优势
空值容易引发错误的原因是,人们按照非空的方式去调用空。
rust如何解决的:空与非空也是一种枚举,他就是 Option!
// rust 中对 Option 的定义
enum Option<T> {
Some(T),
None,
}
如果我们写了下面的函数,并尝试运行
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
这不会通过!因为 rust 不会把i8和 Option<i8>加在一起。我们需要手动把Option<i8>转换成i8,这样就可以保证,所有可以编译通过的代码不会把空加进去了。
6.2. match 控制流运算符
上边我还在想,定义了枚举类型,怎么进行类型判断,这不就来了
Match Demo( 匹配纯类型的 enum)
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 main() {
println!("{}", value_in_centsPenny);
println!("{}", value_in_centsNickel);
println!("{}", value_in_centsDime);
}
Match Demo( 匹配有数据的 enum)
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// ....
}
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
}
}
}
fn main() {
println!("{}", value_in_centsPenny);
println!("{}", value_in_centsNickel);
println!("{}", value_in_centsAlabama));
}
匹配 Option<T>
之前说的 Option 还没说处理方案:比如可能是空的数要加一,方法为,空还是空,数+1
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
// 这个会报错,因为没写 None,对 enum 的匹配需要是穷尽的
// fn plus_one(x: Option<i32>) -> Option<i32> {
// match x {
// Some(i) => Some(i + 1),
// }
// }
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
通配模式和 _ 占位符
如果不是对 enum 的匹配,比如输入一个数,根据大小进行后续判断。就要用到“other”。
fn main() {
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 的值,也可以用通配符"_"
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
// _ => (), // 可以用空的(),代表“无事发生”
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}
6.3. if let 简单控制流
if let 可以简化代码。可以认为 if let 是 match 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。
if let
复杂的案例
#![allow(unused)]
fn main() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (), // 我们只考虑 3 的情况,但是我们需要写通配符,很麻烦
}
}
使用 if let 进行简化
#![allow(unused)]
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
}
if let else
复杂的情况
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;
}