枚举 enum
,用于从众多选项中选择一个。
定义枚举
#[derive(Debug)]
enum Week {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
fn main() {
let today = Week::Saturday; // 使用枚举
let tomorrow = Week::Sunday;
println!("{:?}", today);
}
这是我们在很多面向对象语言中常见的定义枚举的方式
以往对枚举的认识,就是枚举限定了几个固定的选项,我们只能使用众多选项中的一个,或者说,只是使用了某一个元素的名字,例如上面Week枚举中的Monday,至于Monday是什么,无所谓,Monday = 1也好,Monday = 10也好,我们并不关心。但是对于Rust,我们对枚举有更进一步的应用。
用枚举代替结构体
#[derive(Debug)]
enum IpAddr {
V4(u8, u8, u8, u8), // 这个枚举成员是四个u8类型的元祖
V6(String), // 这个枚举成员是String类型
}
fn main() {
// 定义一个枚举变量,并将四个值存放
let loopbackV4 = IpAddr::V4(127, 0, 0, 1);
// 定义另一个枚举变量,存入一个 String 类型的值
// "xxx".to_string() 方法和 String::from("xxx") 是一样的效果
let loopbackV6 = IpAddr::V6("::1".to_string());
println!("{:?}\n{:?}", loopbackV4, loopbackV6);
}
上面的代码,我们可以给枚举的每一个成员,指定一个数据类型,并且在创建一个枚举变量的时候,将某个值存入枚举。在 struct
中可以存储不同类型的变量,现在,在枚举中也可以。
再来看一个例子
// 定义一个操作枚举
#[derive(Debug)]
enum Operation {
Move {x: i32, y:i32},
Jump(u32),
Attack(i32),
}
fn main() {
// 定义一个移动的操作
let opt_move = Operation::Move {x: 10, y: 11};
// 定义一个攻击的操作
let opt_attack = Operation::Attack(100);
// 定义一个跳跃的操作
let opt_jump = Operation::Jump(3);
DoOperation(opt_move);
DoOperation(opt_attack);
DoOperation(opt_jump);
}
// 执行操作
fn DoOperation(opt: Operation) {
println!("Do operation: {:?}", opt);
}
上面的代码我们定义了一个 Operation 枚举,里面有移动,攻击和跳跃三种操作方式。在 main 中定义了三个操作的变量,并且将每一次操作的具体值直接附加到了枚举成员上,例如 opt_attack 攻击操作,这次操作的伤害是100。
Rust 中使用
enum
代替struct
将获得更简洁的代码。并且,每个枚举成员可以处理不同类型和数量的数据。
Rust 的 Option 枚举解释
Rust 中没有 Null
值,无法将一个变量赋值为 Null
, 例如 let a = Null;
,这样的操作在Rust中不存在。但是Rust中有 Option
枚举,这个枚举,用于表示 存在
与 不存在
的概念。有点抽象,没关系,一步一步来,先看下 Option
源代码的定义
enum Option<T> {
Some(T),
None,
}
这里的 <T>
是指可以代表任何数据类型的,这是范型相关的东西,后面会学习。可以将 Option
枚举想象成可以装不同类型东西的小盒子,例如我们定义了一个装玩具汽车的小盒子,这个小盒子里只能装玩具汽车。任何时候,只要这个盒子存在,那么里面就会有两种状态,要么有玩具汽车,要么没有玩具汽车。在有些面向对象的语言中,如果访问一个玩具汽车,而恰好当时那里没有玩具汽车,那么就会造成空引用
,如果没有手动处理空引用的情况,则程序就会出现Bug。而Rust则避免了 空引用
的情况。
看下面的代码
fn main(){
// 使用 Option 将一个 String 类型的值包起来
let name: Option<String> = Some("Fred".to_string());
}
Option
用于某些地方可能存在有值或没值的情况。Option 及成员已经被自动包含,所以我们不需要Option::Some(xxx)
这样来使用。
match 匹配
对于 enum
类型的值,我们不能直接比较,看下面的代码,是无法编译通过的。
let name: Option<String> = Option::Some("Jack".to_string());
println!(name == "Jack".to_string());
上面代码中 name == "Jack".to_string()
编译出错,因为 == 两边的数据类型不一样。这里,我们就可以用到 match
。
看下面的代码
#[derive(Debug)]
enum Operation {
Move {x: i32, y:i32},
Jump(u32),
Attack(i32),
Talk(String),
}
fn main() {
let opt_talk = Operation::Talk("Hello".to_string());
let opt_move = Operation::Move { x: 10, y: 20 };
match opt_move {
Operation::Talk(ref value) => { // 这里加了 ref 是为了避免所有权转移
println!("Talk: {:?}", value);
},
Operation::Move {x,y} => {
println!("Move, x: {}, y: {}", x, y);
}
_ => {
// nothing
}
}
}
上面的代码中,match Operation 枚举时,并没有匹配所有的情况,所以最后需要 _ =>
,相当于某些编译语言中 switch 中的 default
,即在上面的情况都不匹配的情况下,执行的操作。
if let 使用
直接看代码,简化上面 match
的操作
if let Operation::Move{x, y} = opt_move {
println!("Move, x: {}, y: {}", x, y);
} else {
println!("nothing");
}
这样就可以不用 match
直接匹配枚举中的某一个成员类型。
感觉这篇博客有些地方写的可能不是很清楚,说明我对这块知识的理解程度还不够。下面是一些讲解 Rust 枚举的链接
https://rustwiki.org/zh-CN/rust-by-example/custom_types/enum.html
https://www.twle.cn/c/yufei/rust/rust-basic-enums.html
http://www.ameyalokare.com/rust/2017/10/23/rust-options.html
网友评论