和C语言一样Rust也提供了枚举类型,我们通常也简称为枚举,它允许我们列举所有可能的值来定义一个类型。在这篇文章里,我们首先会简单的介绍和定义并且使用枚举,向你展示枚举是如何连同数据来一起编码信息的。接着来看看Rust标准库中提供的枚举类型,Option,它常常被用来描述某些可能不存在的值。随后,我们将学会如何在match表达式中使用模式匹配,并根据不同的枚举值来执行不同的代码。最后,我们还会介绍另外一种常用的结构if let,它可以在某些场景下简化我们处理枚举的代码。
定义和使用枚举Rust使用enum关键字来定义一个枚举,这点跟C语言有点类似,enum后台紧跟枚举的名称,然后是一对花括号,花括号里存放枚举成员(rust里叫做变体)具体定义语法如下所示:
enum enum_name {
// variant
},
定义枚举的例子如下所示:
enum Color {
Red,
Blue,
Yellow,
}
fn main() {
println!("hello world!");
}
2. 使用枚举类型
#[derive(Debug)] // 为了打印正常需要使用这个宏
enum Color {
Red,
Blue,
Yellow,
}
fn main() {
println!("the color = {:?}", Color::Red); // 访问枚举成员使用双冒号(::)
println!("the color = {:?}", Color::Blue);
println!("the color = {:?}", Color::Yellow);
}
3. 在结构体中嵌入枚举类型
Rust结构体中是允许我们嵌入枚举类型,就像下面所示:
#[derive(Debug)]
enum Color {
Red,
Blue,
Yellow,
}
#[derive(Debug)]
struct Canvas {
name: String,
color: Color,
}
fn main() {
println!("the color = {:?}", Color::Red);
println!("the color = {:?}", Color::Blue);
println!("the color = {:?}", Color::Yellow);
let c = Canvas {
name: String::from("canvas"),
color: Color::Red,
};
println!("the canvas {:?}", c);
println!("the canvas name is {}", c.name);
println!("the canvas color is {:?}", c.color);
}
4. 为枚举类型添加方法
跟结构体一样我们同样的可以为枚举类型定义方法,具体如下所示:
#[derive(Debug)]
enum Color {
Red,
Blue,
Yellow,
}
impl Color {
fn print_red_color(&self) {
println!("the color is {:?}", Self::Red); // 注意Self中S是大写的
}
}
fn main() {
let cc = Color::Red;
cc.print_red_color();
Color::Red.print_red_color();
}
Option枚举类型
Option枚举定义在Rust标准库当中,是预导入模块(就是不用导入就可以直接使用的),他描述了某个值(或者类型)可能存在,或不存在的情况。
Option枚举的原型如下所示:
enum Option<T> {
Some(T),
None,
}
这里的<T>是Rust中一个泛型语法,这里不细讲。Option枚举中的Some变体可以包含任意类型的数据,None表示为空。他的用法如下所示:
fn main() {
let some_value_int = Some(21);
let some_value_str = Some("rust string");
let some_option: Option<u32> = Some(23);
println!("the some value int = {:?}", some_value_int);
println!("the some value str = {:?}", some_value_str);
println!("the some option value = {:?}", some_option);
}
控制流运算符match
match是Rust中一个异常强大的控制流运算符,它允许将一个值与一系列的模式相比较,并根据匹配的模式执行相应代码块。模式可以是由字面量,变量名,通配符和许多其他的东西组成。具体我们用一个例子来说明一些,代码如下所示:
#[derive(Debug)]
enum Color {
Red,
Blue,
Yellow,
}
fn match_color(color: Color) -> u32 {
match color {
Color::Red => 0,
Color::Blue => 1,
Color::Yellow => 2,
}
}
fn main() {
let red = match_color(Color::Red);
let blu = match_color(Color::Blue);
let yel = match_color(Color::Yellow);
println!("the color is {:?}", red);
println!("the color is {:?}", blu);
println!("the color is {:?}", yel);
}
输出结果如下所示:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized debuginfo] target(s) in 0.56s
Running `target/debug/playground`
Standard Output
the color is 0
the color is 1
the color is 2
match表达式执行时,它会将产生的结果值依次与每个分支中的模式相比较。假如模式匹配成功,则与该模式相关联的代码就会被继续执行。而假如模式匹配失败,则会继续执行下一个分支。每个分支所关联的代码同时也是一个表达式,而这个表达式运行所得到的结果值,同时也会被作为整个match表达式的结果返回。
2. match匹配绑定值的模式
match运算符匹配分支另外一个有趣的地方在于它们可以绑定被匹配对象的部分值,而这也正是我们用于从枚举变体中提取值的方法。意思是将一个枚举里面变体存在值的使用进行匹配的一种模式,具体看一个例子就明白了,代码如下所示:
#[derive(Debug)]
enum Color {
Red,
Blue,
Yellow,
Rgba(Rgb),
}
#[derive(Debug)]
enum Rgb {
White,
Pink,
}
fn match_color(color: Color) -> u32 {
match color {
Color::Red => 0,
Color::Blue => 1,
Color::Yellow => 2,
Color::Rgba(value) => {
println!("the value is {:?}", value);
255
}
}
}
fn main() {
let red = match_color(Color::Red);
let blu = match_color(Color::Blue);
let yel = match_color(Color::Yellow);
let pink = match_color(Color::Rgba(Rgb::Pink));
let white = match_color(Color::Rgba(Rgb::White));
println!("the color is {:?}", red);
println!("the color is {:?}", blu);
println!("the color is {:?}", yel);
println!("the color is {:?}", pink);
println!("the color is {:?}", white);
}
匹配Option<T>
当我们想要在使用Option<T>时,从Some中取出内部的T值,这时就可以使用match匹配控制运算符来处理Option<T>。具体操作如下所示:
fn get_option_value(value: Option<u32>) -> Option<u32> {
match value {
Some(v) => Some(v * 2),
None => None,
}
}
fn main() {
let x:Option<u32> = Some(23);
let y:Option<u32> = Some(33);
let x:Option<u32> = get_option_value(x);
let y:Option<u32> = get_option_value(y);
let z = x.unwrap() y.unwrap(); // unwrap方法用于取出Some(x)中x的值
// 感觉这种写法有点丑陋
println!("z = {}", z);
}
匹配必须穷举所有的可能
match表达式中有一个需要注意的特性,Rust中的匹配是穷尽的,我们必须穷尽所有的可能性,来确保代码是合法有效的。如果我们将上面例子中的get_option_value函数中匹配None的情况注释掉,就会出现如下结果。所以我们要明确地处理值为None的情形。
fn get_option_value(value: Option<u32>) -> Option<u32> {
match value {
Some(v) => Some(v * 2),
//None => None,
}
}
fn main() {
let x:Option<u32> = Some(23);
let y:Option<u32> = Some(33);
let x:Option<u32> = get_option_value(x);
let y:Option<u32> = get_option_value(y);
let z = x.unwrap() y.unwrap(); // unwrap方法用于取出Some(x)中x的值
// 感觉这种写法有点丑陋
println!("z = {}", z);
}
出错结果如下所示:
Compiling playground v0.0.1 (/playground)
error[E0004]: non-exhaustive patterns: `None` not covered
--> src/main.rs:2:11
|
2 | match value {
| ^^^^^ pattern `None` not covered
|
note: `Option<u32>` defined here
--> /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/option.rs:562:1
::: /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/option.rs:566:5
|
= note: not covered
= note: the matched value is of type `Option<u32>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
3 ~ Some(v) => Some(v * 2),
4 ~ None => todo!(),
|
For more information about this error, try `rustc --explain E0004`.
error: could not compile `playground` due to previous error
通配符(_)
_模式可以匹配任何值,这相当于C语言中switch语句中的default了。我们可以通过将它放置于其他分支后,使其帮我们匹配所有没有被显式指定出来的可能的情形。与它对应的代码块里只有一个()空元组,所以在_匹配下什么都不会发生。
fn get_option_value(value: Option<u32>) -> Option<u32> {
match value {
Some(v) => Some(v * 2),
_ => None, // 使用通配符处理值为None的情况
}
}
fn main() {
let x:Option<u32> = Some(23);
let y:Option<u32> = Some(33);
let x:Option<u32> = get_option_value(x);
let y:Option<u32> = get_option_value(y);
let z = x.unwrap() y.unwrap(); // unwrap方法用于取出Some(x)中x的值
// 感觉这种写法有点丑陋
println!("z = {}", z);
}
if let简单控制流
if let处理只关心一种匹配而忽略其他匹配的情况。使用if let意味着我们可以编写更少的代码,使用更少的缩进,使用更少的模板代码。但是,我们也放弃了match所附带的穷尽性的检查。我们可以将if let看作match的语法糖。它只在值满足某一特定模式时才运行的代码,而忽略其他所有的可能性。使用例子如下所示:
fn main() {
let x = Some(32);
if let Some(32) = x {
println!("the x = {}", x.unwrap());
} else {
println!("none value"); // 我们可以使用else分支来处理其他未知的情况
}
}
小结
在这篇章中我们知道怎么在Rust中定义枚举及其使用,介绍了标准库中Option枚举类型及怎么利用它来避免未知的错误,最后讲了match和if let这两种模式匹配,并利用这两种匹配模式来获取Option中的Some变体中的值。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved