枚举(enum)允许我们定义一个类型,该类型的值只能是一组可能变体中的一个。
enum IpAddrKind {
V4,
V6,
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
route(IpAddrKind::V4);
}
fn route(ip_kind: IpAddrKind) {}
注意枚举值通过命名空间语法来访问:IpAddrKind::V4
。
枚举变体可以携带各种类型的数据:
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
每个变体可以有不同类型和数量的数据:
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
复杂的例子:
enum Message {
Quit, // 没有关联数据
Move { x: i32, y: i32 }, // 包含匿名结构体
Write(String), // 包含单个String
ChangeColor(i32, i32, i32), // 包含三个i32值
}
可以使用impl
关键字为枚举定义方法:
impl Message {
fn call(&self) {
// 方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();
Rust标准库定义的Option<T>
枚举表示一个值可能存在(Some
)或不存在(None
)。
enum Option<T> {
None,
Some(T),
}
Option<T>
已包含在预导入模块中,无需显式引入作用域,可以直接使用Some
和None
。
let some_number = Some(5); // 类型为Option<i32>
let some_char = Some('e'); // 类型为Option<char>
let absent_number: Option<i32> = None; // 必须标注类型
Rust不允许将Option<T>
和T
类型直接操作,必须先将Option<T>
转换为T
:
let x: i8 = 5;
let y: Option<i8> = Some(5);
// 错误:不能直接相加不同类型
// let sum = x + y;
这种设计防止了空值引用错误,编译器确保我们处理了值可能不存在的情况。
match
是一个强大的控制流运算符,允许我们将一个值与一系列模式进行比较并执行相应代码。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
每个分支由两部分组成:模式和要执行的代码。多行代码需要用大括号:
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
},
// 其他分支...
}
模式可以绑定匹配值的部分:
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// ...
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
match
可以优雅地处理Option<T>
:
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
match
表达式必须覆盖所有可能的情况,编译器会检查:
// 错误:没有处理None情况
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
// 缺少对None的处理
}
}
使用通配符_
处理其他所有情况:
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(), // 处理所有其他值
}
使用_
表示我们不关心这个值:
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (), // 不做任何操作
}
if let
提供了一种更简洁的方式来处理只关心一种匹配情况的值:
// 使用match
let config_max = Some(3u8);
match config_max {
Some(max) => println!("最大值为 {}", max),
_ => (),
}
// 使用if let(更简洁)
if let Some(max) = config_max {
println!("最大值为 {}", max);
}
if let
可以包含else
:
if let Coin::Quarter(state) = coin {
println!("来自{:?}的25分硬币!", state);
} else {
count += 1;
}
let else
是处理模式不匹配情况的简洁方式,特别适用于”快乐路径”模式:
fn describe_state_quarter(coin: Coin) -> Option<String> {
// 如果不是Quarter类型,直接返回None
let Coin::Quarter(state) = coin else {
return None;
};
// 主要逻辑
if state.existed_in(1900) {
Some(format!("{:?}对美国来说很古老!", state))
} else {
Some(format!("{:?}相对较新.", state))
}
}
不同于if let
,let else
要求else
分支必须从函数返回(或跳出当前作用域)。
枚举和模式匹配是Rust的强大功能:
这些功能使Rust能够在类型系统层面防止错误,并提供清晰、可读的代码结构。
好好学习,天天向上