Enum
其他语言中也有枚举类型,但rust中的Enum还有一些额外的特性,使得它更强大
先从最基础的用法开始,定义一个Payment枚举类型
enum Payment {
Cash,
CreditCard,
}
fn main() {
let some_payment = Payment::Cash;
match some_payment {
Payment::Cash => {
println!("Paying with cash...");
},
Payment::CreditCard => {
println!("Paying with credit card...");
},
}
}
// 此时的用法很简单,来自其他编程语言背景的人,也会感觉很熟悉
// 但如果此时我对enum Payment中加入新的枚举项,会立刻被compiler发现,并提示你match没有考虑到全部的情况
enum Payment {
Cash,
CreditCard,
DebitCard, // 加入 借记卡 方式
}
fn main() {
let some_payment = Payment::Cash;
match some_payment {
Payment::Cash => {
println!("Paying with cash...");
},
Payment::CreditCard => {
println!("Paying with credit card...");
},
}
}
编译器提示,`DebitCard` not covered, 可以看出rust编译器的提示非常详细,是初学rust的好帮手
error[E0004]: non-exhaustive patterns: `DebitCard` not covered
--> src/main.rs:10:11
|
1 | / enum Payment {
2 | | Cash,
3 | | CreditCard,
4 | | DebitCard, // 加入 借记卡 方式
| | --------- not covered
5 | | }
| |_- `Payment` defined here
...
10 | match some_payment {
| ^^^^^^^^^^^^ pattern `DebitCard` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `Payment`
以上是基本用法,rust中enum的强大之处在于,enum中的每个枚举项,还可以关联具体的数据
// 对enum item Cash关联 f32 类型的值
enum Payment {
Cash(f32), // 关联 f32 类型的值
CreditCard,
DebitCard,
}
fn main() {
let some_payment = Payment::Cash(100.); // 定义时,需要指定f32的值
match some_payment {
// compiler知道 amount是 f32 类型
Payment::Cash(amount) => {
println!("Paying with cash in the amount of {}", amount);
},
Payment::CreditCard => {
println!("Paying with credit card...");
},
Payment::DebitCard => {
println!("Paying with debit card...");
},
}
}
不光能关联简单的类型,如上例中的f32, 还可以关联更复杂的类型,比如tuple,struct
// 方法2和3更清晰,每项都有清楚的业务含义
enum Payment {
Cash(f32),
CreditCard(String, f32), // 定义方法1: 使用tuple
DebitCard(DebitData), // 定义方法2: 使用struct
Crypto{account_id: String, amount: f32}, // 定义方法3: 类似于struct
}
struct DebitData {
pub card_number: String,
pub amount: f32,
}
fn main() {
let some_payment = Payment::Cash(100.);
match some_payment {
Payment::Cash(amount) => {
println!("Paying with cash in the amount of {}", amount);
},
Payment::CreditCard(some_string, some_f32) => {
println!("Paying with credit card...some_string {}, some_f32 {}", some_string, some_f32);
},
Payment::DebitCard(data) => {
println!("Paying with debit ... card_number is {}, amount {}", data.card_number, data.amount);
},
Payment::Crypto{account_id, amount} => {
println!("Paying with crypto... account_id is {}, amount {}", account_id, amount);
},
}
}
// 此时因为我们在main()函数中,只定义了 Payment::Cash, Payment的其他项都未被使用,所以compiler会提出这样的警告: `variant is never constructed: `CreditCard``
// 解决办法,可以在暂时不用的enum item前加一个_ (下划线),告知rust compiler,我自己知道,我暂时不用
// 此时编译,会发现只有 DebitCard 和 Crypto 两项提出警告
enum Payment {
Cash(f32),
_CreditCard(String, f32), // 加下划线
DebitCard(DebitData),
Crypto{account_id: String, amount: f32},
}
struct DebitData {
pub card_number: String,
pub amount: f32,
}
fn main() {
let some_payment = Payment::Cash(100.); // 定义时,需要指定f32的值
match some_payment {
Payment::Cash(amount) => {
println!("Paying with cash in the amount of {}", amount);
},
// 此处也需要 下划线
Payment::_CreditCard(some_string, some_f32) => {
println!("Paying with credit card...some_string {}, some_f32 {}", some_string, some_f32);
},
Payment::DebitCard(data) => {
println!("Paying with debit ... card_number is {}, amount {}", data.card_number, data.amount);
},
Payment::Crypto{account_id, amount} => {
println!("Paying with crypto... account_id is {}, amount {}", account_id, amount);
},
}
}
我们将处理payment的逻辑写成一个函数,重新组织之后的代码如下:
// 方法2和3更清晰,每项都有清楚的业务含义
#[derive(Debug)]
enum Payment {
Cash(f32),
CreditCard(String, f32), // 定义方法1: 使用tuple
DebitCard(DebitData), // 定义方法2: 使用struct
Crypto{account_id: String, amount: f32}, // 定义方法3: 类似于struct
}
#[derive(Debug)]
struct DebitData {
pub card_number: String,
pub amount: f32,
}
fn process_payment(some_payment: Payment) {
match some_payment {
Payment::Cash(amount) => {
println!("Paying with cash in the amount of {}", amount);
},
Payment::CreditCard(some_string, _) => {
println!("Paying with credit card...some_string {}", some_string);
},
Payment::DebitCard(data) => {
println!("Paying with debit ... card_number is {}, amount {}", data.card_number, data.amount);
},
Payment::Crypto{account_id, amount} => {
println!("Paying with crypto... account_id is {}, amount {}", account_id, amount);
},
}
}
fn main() {
let some_payment = Payment::Cash(100.);
process_payment(some_payment);
let cc_payment = Payment::CreditCard("CC Num".to_string(), 250.);
process_payment(cc_payment);
let debit_payment = Payment::DebitCard(DebitData{
card_number: "Debit Num".to_string(),
amount: 400.,
});
process_payment(debit_payment);
let crypto_payment = Payment::Crypto{
account_id: "abc 123".to_string(),
amount: 1000.,
};
process_payment(crypto_payment);
}
Option
Option<T> 是一种常见的 Enum, Option只有两种可能,分别是 Some和None
pattern matching 可以用在普通变量、函数的返回值, tuple, enum, struct上, 还可以搭配if, while使用, 即 if let 语法 和 while let语法
// match 的各个arm中, 使用if
let pair: (i32, i32) = (2, -2);
match pair {
(x, y) if x == y => println!("x == y"),
(x, y) if x + y == 0 => println!("x + y == 0"),
(x, _) if x % 2 == 1 => println!("x is odd"),
_ => println!("default case"),
}
let x: Option<i32> = None; // Some(5)
match x {
Some(v) => println!("Matched {}", v),
None => println!("Default case, x = {:?}", x),
}
// 有时候,上面这种写法太繁琐,就会用到 if let 语法
// 本质上也是一种 pattern matching (但不需要穷举所有情况)
// 不像普通的if 后面表达式求值为一个bool
if let Some(v) = x {
println!("Matched {}", v); //注意是分号
} else {
println!("Default case, x = {:?}", x);
}
// 上面的 if let看起来跟原始的match相比,并没有简化
// 实际上 if let可以不跟else使用,而不引起编译器报错,但match必须穷举完所有的情况
if let Some(v) = x {
println!("Matched {}", v);
}
// while let 语法
// while let 也是一种 pattern matching
// 不像普通的while后面跟着一个bool 表达式
fn main() {
let mut count: Option<i32> = Some(0); // mutable类型,后面会修改
while let Some(v) = count {
if v > 10 {
count = None;
} else {
println!("Count is {}", v);
count = Some(v + 1);
}
}
}
参考 [1] www.youtube.com/watch?v=N28…