枚举在 Rust 中是非常常用的一个特性,经常会配合 match 使用。
枚举定义
枚举的值可以定义成不同的类型或者不定义,比如 String ,struct ,甚至是其他枚举。
#[derive(Debug)]enum MyEnum {A,B(String),C { x: u8, y: u8 },}fn main() {let a = MyEnum::A;println!("{:?}", a);let b = MyEnum::B(String::from("hello"));println!("{:?}", b);let c = MyEnum::C{ x: 1, y: 1 };println!("{:?}", c);}
上面的代码打印出来是这样的。
枚举比较
match
枚举的比较,甚至是想获得枚举里的值内容,就得用到 match 了。
#[derive(Debug)]enum MyEnum {A,B(String),C { x: u8, y: u8 },D,}fn main() {println!("{}", test_enum(MyEnum::A));println!("{}", test_enum(MyEnum::B(String::from("hello"))));println!("{}", test_enum(MyEnum::C{ x: 1, y: 1 }));println!("{}", test_enum(MyEnum::D));}fn test_enum(b: MyEnum) -> String {match b {MyEnum::A => String::from("world"),MyEnum::B(str) => str,MyEnum::C { x, y } => (x + y).to_string(),_ => String::from("any"),}}
match 会进行逐个匹配,并且执行匹配命中的函数,在函数中可以获取到枚举里的值,并进行逻辑操作。其中 _ => () 中的 _ 代表通配,也就不需要把 enum 中的所有可能存在的值都写一遍。
match 不仅仅可以用于 enums ,还能用于其他类型,比如下面代码 match 整型和字符串,用
|可以匹配多个。所以 match 非常强大。
fn test_num(n: u32) -> u32 {match n {1 | 2 => 1, // 1 或者 2 都返回 1_ => 0, // 兜底返回 0}}fn test_str(n: &str) -> u32 {match n {"1" => 1, // 1 或者 2 都返回 1_ => 0, // 兜底返回 0}}
if let
除了可以用 match 进行比较之外,还能用 if let ,在部分场景用 if let 会让代码更简洁一些( 因为不需要写 _ => () 了,两者区别就类似于 JS 中是选 if else 还是 switch case )。所以选择哪种就看场景适合了。
#[derive(Debug)]enum MyEnum {A,B(String),C { x: u8, y: u8 },D,}fn main() {println!("{}", test_enum_2(MyEnum::A));println!("{}", test_enum_2(MyEnum::B(String::from("hello"))));println!("{}", test_enum_2(MyEnum::C{ x: 1, y: 1 }));}fn test_enum_2(b: MyEnum) -> String {if let MyEnum::A = b {String::from("world")} else if let MyEnum::B(str) = b {// if let 也是可以拿到枚举值的str} else {String::from("any")}}
定义方法
Enum 跟 Struct 一样,也可以额外定义方法。
enum MyEnum {A,B(String),C { x: u8, y: u8 },D,}impl MyEnum {fn test_fn(&self) -> u8 {// 可以在函数里做一些 match 操作match &self {&Self::A => 1,&Self::B(_str) => 2,_ => 0,}}}fn main() {let a = MyEnum::A;println!("{}", a.test_fn());}
在 Enum 实例的方法中,可以用 &Self::A 来替代 &MyEnum::A 的写法。这里有一点要注意的是,实例方法里的入参是引用,因此 match 里的匹配方法里的枚举值,都会是引用,比如 &Self::B 的枚举值就是 &String 了。
Option
Rust 内置了一些枚举,Option
enum Option<T> {None,Some(T),}
之所以有这个枚举的存在,是因为在 Rust 中是没有 Null 的,而是通过 Option
而之所以这么设计的原因,是因为 Rust 觉得提供 Null 会比较容易产生错误( 比如直接使用可能为 Null 的值 ),但是又确实存在这种空与非空的场景,所以就提供了这么个特殊的枚举,正因为它是枚举类型,跟普通类型是不一样的,所以没法直接拿来就用。
比如下面这种用法就报错了,因为 Option
let x: i8 = 5;let y: Option<i8> = Some(5);let sum = x + y;
正常的用法大概就类似于下面这种,某个函数返回的值可能是空的,那么就可以定义返回类型为 Option<T> ,调用该函数拿到值的时候,再通过 match 来获取到返回的非空值。
可以留意到,下面的代码没有用
Option::Some,而是直接用 Some 和 None ,因为 Option是特殊的,可以直接用 Some 及 None 。
use rand::Rng;#[derive(Debug)]enum MyEnum {A,B(String),C { x: u8, y: u8 },D,}fn main() {// 随机取个 enum 传入函数let arr = [ MyEnum::A, MyEnum::B(String::from("hello")), MyEnum::D ];let result = may_be_null(&arr[rand::thread_rng().gen_range(0..arr.len())]);// 判断是否非空match result {Some(n) => {// n 为非空的值println!("result is {}", n);}None => {println!("result is Null");}}}fn may_be_null(b: &MyEnum) -> Option<u32> {match b {// 仅 A 和 B 才返回结果,其他返回 NoneMyEnum::A => Some(1),MyEnum::B(_str) => Some(2),_ => None,}}
