主题: - 枚举与模式匹配
第一层:原理层(Why & How)
为什么需要枚举?
在编程中,我们经常需要表示一组有限的、相关的值。例如:
- 订单状态:待支付、已支付、已发货、已完成
- 颜色:红、绿、蓝
- IP 地址类型:IPv4 或 IPv6
传统方式的问题:
// 使用常量 - 类型不安全,容易混淆
const PENDING: u8 = 0;
const PAID: u8 = 1;
const SHIPPED: u8 = 2;
fn process_order(status: u8) {
// 可以传入任意 u8 值,包括无效的 100
if status == PENDING { /* ... */ }
}
枚举解决方案:
enum OrderStatus {
Pending,
Paid,
Shipped,
Completed,
}
fn process_order(status: OrderStatus) {
// 只能传入 OrderStatus 类型的值
match status {
OrderStatus::Pending => /* ... */,
OrderStatus::Paid => /* ... */,
// 编译器会检查是否覆盖所有变体
}
}
Rust 枚举的独特之处
Rust 的枚举(enum)与其他语言有本质区别:
| 特性 | Rust 枚举 | C/C++ 枚举 | Java 枚举 |
|---|---|---|---|
| 变体数据 | 每个变体可携带不同类型和数量的数据 | 仅整数常量 | 可携带数据,但类型受限 |
| 代数数据类型 | 是(Algebraic Data Type) | 否 | 部分支持 |
| 内存布局 | 标签 + 联合体(tagged union) | 整数 | 对象引用 |
| 模式匹配 | 原生支持,编译器检查穷尽性 | 需手动 switch | 支持 switch |
代数数据类型(ADT)概念:
代数数据类型 = 乘积类型(Product Type)+ 和类型(Sum Type)
乘积类型(Product Type):同时包含多个值
- 结构体:struct Point { x: i32, y: i32 } 包含 x AND y
- 内存:x 和 y 同时存在
和类型(Sum Type):可能是多个值中的一个
- 枚举:enum Shape { Circle(f64), Rectangle(f64, f64) }
- 内存:要么是 Circle,要么是 Rectangle,不会同时存在
枚举的内存布局
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 单个值
ChangeColor(u8, u8, u8), // 多个值
}
内存表示:
┌─────────────────────────────────────────────────────┐
│ Message 枚举 │
├─────────────────────────────────────────────────────┤
│ 标签(discriminant) │
│ ├── 0: Quit │
│ ├── 1: Move { x, y } │
│ ├── 2: Write(String) │
│ └── 3: ChangeColor(u8, u8, u8) │
├─────────────────────────────────────────────────────┤
│ 数据(联合体) │
│ ├── Quit: [空] │
│ ├── Move: [x: i32, y: i32] (8 bytes) │
│ ├── Write: [String 结构体] (24 bytes) │
│ └── ChangeColor: [u8, u8, u8, pad] (4 bytes) │
├─────────────────────────────────────────────────────┤
│ 总大小 = max(变体大小) + 标签大小 │
│ Message 大小 = 24 (String) + 1 (标签+对齐) = 32 │
└─────────────────────────────────────────────────────┘
内存优化 - 空指针优化(NPO):
enum Option<&T> {
Some(&T), // 非空指针
None, // 空指针
}
// 优化后:Option<&T> 大小与 &T 相同!
// 使用空指针表示 None,无需额外标签
Option 枚举的设计哲学
Rust 没有空指针(null),使用 Option<T> 显式处理可能缺失的值:
enum Option<T> {
None, // 值不存在
Some(T), // 值存在
}
与 null 的对比:
其他语言(Java/C++):
String s = null; // 可以赋值为 null
s.length(); // 运行时崩溃!NullPointerException
Rust:
let s: Option<String> = None; // 显式标记可能为空
s.len(); // 编译错误!不能直接在 Option 上调用
// 正确处理
match s {
Some(ref str) => println!("{}", str.len()),
None => println!("No string"),
}
哲学:将运行时错误转为编译时错误
Result 枚举的错误处理
enum Result<T, E> {
Ok(T), // 操作成功,包含返回值
Err(E), // 操作失败,包含错误信息
}
与异常机制的对比:
| 特性 | Rust Result | Java/C++ 异常 | Go 多返回值 |
|---|---|---|---|
| 显式性 | 必须处理 | 可忽略 | 可忽略 |
| 性能 | 零成本抽象 | 有开销(栈展开) | 零成本 |
| 类型安全 | 编译时检查 | 运行时捕获 | 无类型约束 |
| 组合能力 | 强大的组合子(map/and_then) | 有限 | 手动检查 |
模式匹配的本质
模式匹配是 Rust 的核心特性,它不仅仅是 switch 语句的增强:
match value {
pattern1 => expression1,
pattern2 => expression2,
_ => default_expression,
}
匹配原理:
┌─────────────────────────────────────────────┐
│ 模式匹配过程 │
├─────────────────────────────────────────────┤
│ 1. 计算 value 的值 │
│ 2. 按顺序匹配 pattern │
│ 3. 第一个匹配的 pattern 被执行 │
│ 4. 绑定 pattern 中的变量 │
│ 5. 执行对应的 expression │
└─────────────────────────────────────────────┘
穷尽性检查(Exhaustiveness Checking):
enum Color {
Red,
Green,
Blue,
}
fn describe(c: Color) -> &'static str {
match c {
Color::Red => "红色",
Color::Green => "绿色",
// 编译错误!缺少 Color::Blue 的处理
}
}
编译器会确保 match 覆盖所有可能的情况,防止遗漏。
第二层:实战层(What & Do)
1. 定义和使用枚举
基础枚举
// 定义枚举
enum Direction {
North,
South,
East,
West,
}
fn main() {
// 创建枚举值
let dir = Direction::North;
// 使用 match 处理
match dir {
Direction::North => println!("向北走"),
Direction::South => println!("向南走"),
Direction::East => println!("向东走"),
Direction::West => println!("向西走"),
}
}
带数据的枚举
enum Message {
// 无数据
Quit,
// 匿名结构体
Move { x: i32, y: i32 },
// 单个值
Write(String),
// 多个值
ChangeColor(u8, u8, u8),
}
fn process_message(msg: Message) {
match msg {
Message::Quit => {
println!("退出程序");
}
Message::Move { x, y } => {
println!("移动到坐标 ({}, {})", x, y);
}
Message::Write(text) => {
println!("写入文本: {}", text);
}
Message::ChangeColor(r, g, b) => {
println!("改变颜色为 RGB({}, {}, {})", r, g, b);
}
}
}
fn main() {
let msg1 = Message::Quit;
let msg2 = Message::Move { x: 10, y: 20 };
let msg3 = Message::Write(String::from("Hello"));
let msg4 = Message::ChangeColor(255, 0, 0);
process_message(msg1);
process_message(msg2);
process_message(msg3);
process_message(msg4);
}
为枚举定义方法
enum Shape {
Circle(f64), // 半径
Rectangle(f64, f64), // 宽, 高
Triangle(f64, f64, f64), // 三边
}
impl Shape {
// 计算面积
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle(a, b, c) => {
// 海伦公式
let s = (a + b + c) / 2.0;
(s * (s - a) * (s - b) * (s - c)).sqrt()
}
}
}
// 判断是否是圆形
fn is_circle(&self) -> bool {
matches!(self, Shape::Circle(_))
}
}
fn main() {
let circle = Shape::Circle(5.0);
let rect = Shape::Rectangle(4.0, 5.0);
println!("圆面积: {:.2}", circle.area());
println!("矩形面积: {:.2}", rect.area());
println!("是圆形吗? {}", circle.is_circle());
}
2. Option 枚举实战
基本使用
fn find_char(s: &str, c: char) -> Option<usize> {
for (i, ch) in s.chars().enumerate() {
if ch == c {
return Some(i);
}
}
None
}
fn main() {
let s = "Hello, World!";
match find_char(s, 'W') {
Some(index) => println!("找到字符,位置: {}", index),
None => println!("未找到字符"),
}
// 使用 if let 简化
if let Some(index) = find_char(s, 'X') {
println!("找到字符,位置: {}", index);
} else {
println!("未找到字符");
}
}
Option 的组合方法
fn main() {
let some_number = Some(5);
let some_string = Some("Hello");
let absent_number: Option<i32> = None;
// map: 转换 Some 中的值
let doubled = some_number.map(|x| x * 2);
println!("{:?}", doubled); // Some(10)
// unwrap_or: 提供默认值
let result = absent_number.unwrap_or(0);
println!("{}", result); // 0
// unwrap_or_else: 惰性计算默认值
let result = absent_number.unwrap_or_else(|| {
println!("计算默认值");
42
});
println!("{}", result); // 42
// and_then: 链式操作(类似 flatMap)
let result = some_number.and_then(|x| {
if x > 0 {
Some(x * 2)
} else {
None
}
});
println!("{:?}", result); // Some(10)
// or_else: 提供备选 Option
let result = absent_number.or_else(|| Some(100));
println!("{:?}", result); // Some(100)
// filter: 过滤不符合条件的值
let result = some_number.filter(|&x| x > 10);
println!("{:?}", result); // None
// take: 取出值,留下 None
let mut some_value = Some(5);
let taken = some_value.take();
println!("taken: {:?}", taken); // Some(5)
println!("remaining: {:?}", some_value); // None
// replace: 替换值,返回旧值
let mut some_value = Some(5);
let old = some_value.replace(10);
println!("old: {:?}", old); // Some(5)
println!("new: {:?}", some_value); // Some(10)
}
解构 Option
fn main() {
let x = Some(5);
let y = Some(10);
let none: Option<i32> = None;
// 解构 Some
match x {
Some(value) => println!("值是 {}", value),
None => println!("没有值"),
}
// 嵌套解构
let nested = Some(Some(42));
match nested {
Some(Some(v)) => println!("嵌套值: {}", v),
Some(None) => println!("内部是 None"),
None => println!("外部是 None"),
}
// 使用 ? 运算符(只能在返回 Option 的函数中使用)
fn add_options(a: Option<i32>, b: Option<i32>) -> Option<i32> {
let a_val = a?; // 如果 a 是 None,提前返回 None
let b_val = b?; // 如果 b 是 None,提前返回 None
Some(a_val + b_val)
}
println!("{:?}", add_options(x, y)); // Some(15)
println!("{:?}", add_options(x, none)); // None
}
3. Result 枚举实战
基本使用
use std::fs::File;
use std::io::Error;
fn open_file(path: &str) -> Result<File, Error> {
File::open(path)
}
fn main() {
match open_file("example.txt") {
Ok(file) => println!("文件打开成功"),
Err(error) => println!("打开失败: {}", error),
}
}
Result 的组合方法
use std::fs::File;
use std::io::{self, Read};
fn main() -> io::Result<()> {
// map: 转换成功值
let result: Result<i32, &str> = Ok(5);
let mapped = result.map(|x| x * 2);
println!("{:?}", mapped); // Ok(10)
// map_err: 转换错误值
let result: Result<i32, &str> = Err("error");
let mapped_err = result.map_err(|e| format!("失败: {}", e));
println!("{:?}", mapped_err); // Err("失败: error")
// and_then: 链式操作
let result: Result<i32, &str> = Ok(5);
let chained = result.and_then(|x| {
if x > 0 {
Ok(x * 2)
} else {
Err("负数")
}
});
println!("{:?}", chained); // Ok(10)
// unwrap: 提取成功值(失败时 panic)
// let value = Ok(5).unwrap(); // 5
// let value = Err("error").unwrap(); // panic!
// expect: 提供自定义 panic 信息
// let value = Err("error").expect("不应该失败"); // panic!
// unwrap_or: 提供默认值
let value = Err("error").unwrap_or(0);
println!("{}", value); // 0
// unwrap_or_else: 惰性计算默认值
let value = Err("error").unwrap_or_else(|e| {
println!("错误: {}", e);
42
});
println!("{}", value); // 42
// 使用 ? 运算符(只能在返回 Result 的函数中使用)
let mut file = File::open("example.txt")?; // 错误时提前返回
let mut contents = String::new();
file.read_to_string(&mut contents)?;
println!("内容: {}", contents);
Ok(())
}
自定义错误类型
use std::fmt;
use std::error::Error;
// 自定义错误类型
#[derive(Debug)]
struct MyError {
message: String,
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MyError: {}", self.message)
}
}
impl Error for MyError {}
// 使用自定义错误
fn divide(a: i32, b: i32) -> Result<i32, MyError> {
if b == 0 {
Err(MyError {
message: String::from("除数不能为零"),
})
} else {
Ok(a / b)
}
}
fn main() {
match divide(10, 2) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
match divide(10, 0) {
Ok(result) => println!("结果: {}", result),
Err(e) => println!("错误: {}", e),
}
}
4. 模式匹配详解
match 表达式
fn main() {
// 匹配数值
let number = 13;
match number {
1 => println!("一"),
2 | 3 | 5 | 7 | 11 | 13 => println!("质数"),
13..=19 => println!("青少年"), // 范围匹配
_ => println!("其他数字"),
}
// 匹配枚举
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState), // 携带数据
}
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("州25分硬币: {:?}", state);
25
}
}
}
let coin = Coin::Quarter(UsState::Alaska);
println!("价值: {} 分", value_in_cents(coin));
}
解构模式
fn main() {
// 解构结构体
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 0, y: 7 };
match point {
Point { x, y: 0 } => println!("在 x 轴上,x = {}", x),
Point { x: 0, y } => println!("在 y 轴上,y = {}", y),
Point { x, y } => println!("在任意位置 ({}, {})", x, y),
}
// 解构枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => println!("退出"),
Message::Move { x: 0, y } => println!("向上移动 {}", y),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("文本: {}", text),
Message::ChangeColor(r, g, b) => println!("颜色: RGB({}, {}, {})", r, g, b),
}
// 解构元组
let tuple = (1, 2, 3);
match tuple {
(0, y, z) => println!("第一个是 0,后面是 {} {}", y, z),
(x, 0, z) => println!("第二个是 0,前后是 {} {}", x, z),
(x, y, 0) => println!("第三个是 0,前面是 {} {}", x, y),
(x, y, z) => println!("三个非零: {} {} {}", x, y, z),
}
// 解构数组/切片
let arr = [1, 2, 3];
match arr {
[0, second, third] => println!("第一个是 0,后面 {} {}", second, third),
[first, 0, third] => println!("第二个是 0,前后 {} {}", first, third),
[first, second] => println!("只有两个元素: {} {}", first, second), // 长度不匹配
[first, ..] => println!("至少一个元素,第一个是 {}", first),
[.., last] => println!("至少一个元素,最后一个是 {}", last),
[first, .., last] => println!("至少两个元素,首 {} 尾 {}", first, last),
}
}
绑定模式
fn main() {
// @ 绑定:同时匹配和绑定
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello { id: id_variable @ 3..=7 } => {
println!("找到 id 在 3-7 范围: {}", id_variable);
}
Message::Hello { id: 10..=12 } => {
println!("找到 id 在 10-12 范围");
}
Message::Hello { id } => {
println!("找到其他 id: {}", id);
}
}
}
匹配守卫(Match Guards)
fn main() {
let num = Some(4);
match num {
Some(x) if x < 5 => println!("小于 5: {}", x),
Some(x) => println!("大于或等于 5: {}", x),
None => println!("没有值"),
}
// 多条件守卫
let pair = (2, -2);
match pair {
(x, y) if x == y => println!("相等"),
(x, y) if x + y == 0 => println!("和为零"),
(x, _) if x % 2 == 1 => println!("第一个是奇数"),
_ => println!("不满足任何条件"),
}
}
5. if let 和 while let
if let 简化模式匹配
fn main() {
let some_value = Some(3);
// 使用 match
match some_value {
Some(3) => println!("三是三"),
_ => (),
}
// 使用 if let(只关心一个模式时更简洁)
if let Some(3) = some_value {
println!("三是三");
}
// if let else
let some_value = Some(5);
if let Some(3) = some_value {
println!("三是三");
} else {
println!("不是三");
}
// 解构结构体
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 0, y: 7 };
if let Point { x: 0, y } = point {
println!("在 y 轴上,y = {}", y);
}
}
while let 循环匹配
fn main() {
// 循环处理 Option
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
// 持续 pop 直到 None
while let Some(top) = stack.pop() {
println!("弹出: {}", top);
}
// 处理迭代器
let mut iter = (1..5).into_iter();
while let Some(n) = iter.next() {
println!("迭代: {}", n);
}
}
6. matches! 宏
fn main() {
let x = 5;
// matches! 返回 bool,适合简单匹配
if matches!(x, 1..=10) {
println!("x 在 1-10 范围");
}
// 模式守卫
if matches!(x, n if n > 3) {
println!("x 大于 3");
}
// 枚举匹配
enum Color {
Red,
Green,
Blue,
}
let color = Color::Red;
if matches!(color, Color::Red | Color::Green) {
println!("红色或绿色");
}
}
第三层:最佳实践(Production Ready)
1. 优先使用 Option 而非 null
// ❌ 不推荐:使用 null 表示缺失
fn find_user(id: u64) -> User? { // Rust 没有 null!
// ...
}
// ✅ 推荐:使用 Option 显式表示可能缺失
fn find_user(id: u64) -> Option<User> {
// 如果找到,返回 Some(user)
// 如果没找到,返回 None
}
2. 使用 Result 而非异常
// ❌ 不推荐:抛出异常
fn parse_int(s: &str) -> i32 throws ParseError { // Rust 没有 throws!
// ...
}
// ✅ 推荐:使用 Result
fn parse_int(s: &str) -> Result<i32, ParseIntError> {
s.parse::<i32>()
}
3. 使用 ? 运算符链式处理
use std::fs::File;
use std::io::{self, Read};
// ❌ 不推荐:手动处理每个错误
fn read_file_bad(path: &str) -> io::Result<String> {
let file = match File::open(path) {
Ok(f) => f,
Err(e) => return Err(e),
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Ok(_) => Ok(contents),
Err(e) => Err(e),
}
}
// ✅ 推荐:使用 ? 运算符
fn read_file_good(path: &str) -> io::Result<String> {
let mut file = File::open(path)?; // 失败时自动返回 Err
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
// ✅ 更推荐:一行搞定
fn read_file_best(path: &str) -> io::Result<String> {
std::fs::read_to_string(path)
}
4. 使用组合子而非 match
// ❌ 不推荐:过度使用 match
fn process_option_bad(opt: Option<i32>) -> Option<i32> {
match opt {
Some(x) => match x {
0 => None,
_ => Some(x * 2),
},
None => None,
}
}
// ✅ 推荐:使用组合子
fn process_option_good(opt: Option<i32>) -> Option<i32> {
opt.filter(|&x| x != 0).map(|x| x * 2)
}
// ✅ 更推荐:使用 and_then
fn process_option_best(opt: Option<i32>) -> Option<i32> {
opt.and_then(|x| {
if x == 0 { None } else { Some(x * 2) }
})
}
5. 使用 if let 处理单一情况
// ❌ 不推荐:只关心一个分支却写完整 match
fn handle_message_bad(msg: Message) {
match msg {
Message::Quit => println!("退出"),
_ => (), // 空分支
}
}
// ✅ 推荐:使用 if let
fn handle_message_good(msg: Message) {
if let Message::Quit = msg {
println!("退出");
}
}
6. 定义清晰的错误类型
use std::fmt;
use std::error::Error;
// ✅ 推荐:定义清晰的错误类型
#[derive(Debug, Clone)]
pub enum AppError {
Io(String),
Parse(String),
Network(String),
NotFound(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Io(msg) => write!(f, "IO 错误: {}", msg),
AppError::Parse(msg) => write!(f, "解析错误: {}", msg),
AppError::Network(msg) => write!(f, "网络错误: {}", msg),
AppError::NotFound(msg) => write!(f, "未找到: {}", msg),
}
}
}
impl Error for AppError {}
// 转换其他错误类型
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> Self {
AppError::Io(e.to_string())
}
}
impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self {
AppError::Parse(e.to_string())
}
}
7. 使用 unwrap/expect 的场景
// ⚠️ unwrap/expect 在以下场景可以使用:
// 1. 测试代码
#[test]
fn test_example() {
let result = parse("42").unwrap(); // 测试中可以 panic
assert_eq!(result, 42);
}
// 2. 原型开发
fn prototype() {
let config = load_config().unwrap(); // 快速原型,稍后处理错误
}
// 3. 确定不会失败的情况
fn safe_example() {
let x: i32 = "42".parse().unwrap(); // 我们知道这是有效的
}
// 4. 使用 expect 提供上下文
fn main() {
let file = File::open("config.toml")
.expect("配置文件必须存在,请检查项目根目录");
}
第四层:问题诊断(Troubleshooting)
问题 1:match 穷尽性检查失败
症状:编译错误 "non-exhaustive patterns"
enum Status {
Active,
Inactive,
Pending,
}
fn check_status(s: Status) {
match s {
Status::Active => println!("活跃"),
Status::Inactive => println!("不活跃"),
// 编译错误!缺少 Pending
}
}
诊断:
- 编译器检测到枚举变体未被覆盖
- 必须处理所有变体或使用
_通配符
解决方案:
fn check_status(s: Status) {
match s {
Status::Active => println!("活跃"),
Status::Inactive => println!("不活跃"),
Status::Pending => println!("待处理"),
}
}
// 或者使用通配符
fn check_status(s: Status) {
match s {
Status::Active => println!("活跃"),
_ => println!("其他状态"), // 覆盖剩余所有变体
}
}
问题 2:unwrap 导致 panic
症状:程序运行时崩溃 "called Option::unwrap() on a None value"
fn main() {
let result: Option<i32> = None;
let value = result.unwrap(); // panic!
}
诊断:
- unwrap 在 None 或 Err 上会 panic
- 应使用更安全的替代方法
解决方案:
fn main() {
let result: Option<i32> = None;
// 方案 1:提供默认值
let value = result.unwrap_or(0);
// 方案 2:使用 match
match result {
Some(v) => println!("值: {}", v),
None => println!("没有值"),
}
// 方案 3:使用 if let
if let Some(v) = result {
println!("值: {}", v);
}
// 方案 4:使用 expect(提供自定义信息)
// let value = result.expect("必须提供值");
}
问题 3:Option/Result 链式调用错误
症状:类型不匹配或方法不存在
fn main() {
let opt: Option<i32> = Some(5);
// 错误:Option 没有 len 方法
let len = opt.len(); // 错误!
// 错误:链式调用类型推断失败
let result = opt.map(|x| x.to_string()).map(|s| s.len());
// 类型推断可能失败
}
诊断:
- Option 和 Result 有特定的组合方法
- 需要理解链式调用的类型变化
解决方案:
fn main() {
let opt: Option<i32> = Some(5);
// 正确:先 map 转换类型
let result = opt
.map(|x| x.to_string()) // Option<i32> -> Option<String>
.map(|s| s.len()); // Option<String> -> Option<usize>
println!("{:?}", result); // Some(1)
// 使用 and_then 进行更复杂的转换
let result = opt.and_then(|x| {
if x > 0 {
Some(x.to_string())
} else {
None
}
});
}
问题 4:模式匹配中的所有权转移
症状:match 后原值无法使用
fn main() {
let msg = Message::Write(String::from("Hello"));
match msg {
Message::Write(text) => println!("{}", text),
_ => (),
}
println!("{:?}", msg); // 错误!msg 已被部分移动
}
诊断:
- match 默认会移动值
- 某些变体解构会获取所有权
解决方案:
fn main() {
let msg = Message::Write(String::from("Hello"));
// 方案 1:使用引用
match &msg {
Message::Write(text) => println!("{}", text),
_ => (),
}
println!("{:?}", msg); // 正常使用
// 方案 2:使用 ref 关键字
match msg {
Message::Write(ref text) => println!("{}", text),
_ => (),
}
println!("{:?}", msg); // 正常使用
// 方案 3:Copy 类型自动复制
let num = Some(5);
match num {
Some(x) => println!("{}", x),
None => (),
}
println!("{:?}", num); // 正常,因为 i32 是 Copy 类型
}
问题 5:if let 无法处理多个分支
症状:想处理两种情况,if let 只能处理一种
fn main() {
let opt = Some(5);
// 想同时处理 Some 和 None
if let Some(x) = opt {
println!("有值: {}", x);
}
// 没有 else if let!
}
诊断:
- if let 设计为简化单一模式匹配
- 需要处理多个分支时应使用 match
解决方案:
fn main() {
let opt = Some(5);
// 方案 1:使用 if let + else
if let Some(x) = opt {
println!("有值: {}", x);
} else {
println!("没有值");
}
// 方案 2:使用 match(可以处理多个分支)
match opt {
Some(0) => println!("零"),
Some(x) if x > 10 => println!("大于 10: {}", x),
Some(x) => println!("其他: {}", x),
None => println!("没有值"),
}
}
第五层:权威引用(References)
官方文档
| 资源 | 链接 |
|---|---|
| The Rust Book - Enums | doc.rust-lang.org/book/ch06-0… |
| The Rust Book - Pattern Matching | doc.rust-lang.org/book/ch18-0… |
| Rust Reference - Enum Types | doc.rust-lang.org/reference/i… |
| std::option Module | doc.rust-lang.org/std/option/ |
| std::result Module | doc.rust-lang.org/std/result/ |
标准库文档
// Option 常用方法
impl<T> Option<T> {
// 构造
fn Some(T) -> Option<T>
fn None -> Option<T>
// 提取
fn unwrap(self) -> T
fn unwrap_or(self, default: T) -> T
fn unwrap_or_else<F>(self, f: F) -> T
fn unwrap_default(self) -> T // T: Default
// 转换
fn map<U, F>(self, f: F) -> Option<U>
fn map_or<U, F>(self, default: U, f: F) -> U
fn and_then<U, F>(self, f: F) -> Option<U>
fn or_else<F>(self, f: F) -> Option<T>
fn filter<F>(self, predicate: F) -> Option<T>
// 查询
fn is_some(&self) -> bool
fn is_none(&self) -> bool
fn contains(&self, x: &T) -> bool // T: PartialEq
// 操作
fn take(&mut self) -> Option<T>
fn replace(&mut self, value: T) -> Option<T>
}
// Result 常用方法
impl<T, E> Result<T, E> {
// 构造
fn Ok(T) -> Result<T, E>
fn Err(E) -> Result<T, E>
// 提取
fn unwrap(self) -> T
fn unwrap_err(self) -> E
fn unwrap_or(self, default: T) -> T
fn unwrap_or_else<F>(self, op: F) -> T
// 转换
fn map<U, F>(self, op: F) -> Result<U, E>
fn map_err<F, O>(self, op: O) -> Result<T, F>
fn and_then<U, F>(self, op: F) -> Result<U, E>
fn or_else<F, O>(self, op: O) -> Result<T, F>
// 查询
fn is_ok(&self) -> bool
fn is_err(&self) -> bool
}
错误处理最佳实践
- Rust Error Handling Survey - 错误处理深度分析
- Error Handling in Rust - 错误处理指南
- Failure crate - 错误处理库(已不推荐)
- Thiserror crate - derive 错误类型(推荐)
- Anyhow crate - 动态错误类型(推荐)
社区资源
- Rust by Example - Enums
- Rust by Example - Option
- Rust by Example - Result
- Rust Design Patterns - Newtype
总结
核心概念对比
| 概念 | 说明 | 使用场景 |
|---|---|---|
| enum | 代数数据类型 | 表示有限集合、带数据变体 |
| Option | 可能缺失的值 | 替代 null、安全的可选值 |
| Result<T, E> | 可能失败的操作 | 错误处理、替代异常 |
| match | 穷尽性模式匹配 | 处理所有可能情况 |
| if let | 单一模式匹配 | 只关心一个分支 |
| ? 运算符 | 错误传播 | 链式处理 Result/Option |
Option vs Result
| 特性 | Option | Result |
|---|---|---|
| 语义 | 值存在/不存在 | 操作成功/失败 |
| 变体 | Some(T) / None | Ok(T) / Err(E) |
| 错误信息 | 无 | 有(Err(E)) |
| 典型场景 | 查找、可选配置 | IO、解析、网络请求 |
方法速查表
| 目的 | Option 方法 | Result 方法 |
|---|---|---|
| 提取值 | unwrap, unwrap_or | unwrap, unwrap_or |
| 转换值 | map, and_then | map, and_then |
| 转换错误 | - | map_err |
| 提供备选 | or, or_else | or, or_else |
| 过滤 | filter | - |
| 链式处理 | and_then | and_then |