枚举在 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 才返回结果,其他返回 None
MyEnum::A => Some(1),
MyEnum::B(_str) => Some(2),
_ => None,
}
}