今天我們來學習 Rust 另一個特性 - 列舉(Enums) 和 模式匹配(Pattern Matching)。列舉讓你能夠定義一個型別,該型別可以是幾個可能的變體之一,而模式匹配則讓你能夠安全且優雅地處理所有可能的情況。
考慮以下情況,你想表示一個任務的狀態:
// 使用字串(不安全,容易出錯)
let status = "pending"; // 可能拼錯成 "pendding"
// 使用數字(不直觀)
let status = 1; // 1 表示什麼?
// 使用常數(較好但不夠完善)
const PENDING: i32 = 0;
const IN_PROGRESS: i32 = 1;
const COMPLETED: i32 = 2;
列舉提供了更好的解決方案:
enum TaskStatus {
Pending,
InProgress,
Completed,
}
let status = TaskStatus::Pending; // 型別安全,不會拼錯
#[derive(Debug)]
enum TaskStatus {
Pending, // 待辦
InProgress, // 進行中
Completed, // 已完成
Cancelled, // 已取消
}
fn main() {
let status = TaskStatus::InProgress;
println!("任務狀態: {:?}", status);
// 函式可以接收列舉作為參數
describe_status(status);
}
fn describe_status(status: TaskStatus) {
match status {
TaskStatus::Pending => println!("任務等待開始"),
TaskStatus::InProgress => println!("任務正在進行"),
TaskStatus::Completed => println!("任務已完成"),
TaskStatus::Cancelled => println!("任務已取消"),
}
}
列舉的每個變體可以攜帶不同型別和數量的資料:
#[derive(Debug)]
enum TaskPriority {
Low,
Medium,
High,
Custom(u32), // 自訂優先順序數值
}
#[derive(Debug)]
enum TaskAssignment {
Unassigned,
Individual(String), // 指派給個人
Team(Vec<String>), // 指派給團隊
Department { // 指派給部門(命名欄位)
name: String,
manager: String,
},
}
fn main() {
let priority = TaskPriority::Custom(42);
let assignment = TaskAssignment::Team(vec![
String::from("張小明"),
String::from("李小華"),
String::from("王小強"),
]);
let dept_assignment = TaskAssignment::Department {
name: String::from("開發部"),
manager: String::from("陳經理"),
};
println!("優先順序: {:?}", priority);
println!("指派: {:?}", assignment);
println!("部門指派: {:?}", dept_assignment);
}
match
是 Rust 最強大的控制流運算子之一:
fn get_priority_score(priority: TaskPriority) -> u32 {
match priority {
TaskPriority::Low => 1,
TaskPriority::Medium => 5,
TaskPriority::High => 10,
TaskPriority::Custom(value) => value, // 提取資料
}
}
fn describe_assignment(assignment: TaskAssignment) -> String {
match assignment {
TaskAssignment::Unassigned => {
String::from("未指派任務")
},
TaskAssignment::Individual(name) => {
format!("指派給: {}", name)
},
TaskAssignment::Team(members) => {
format!("指派給團隊: {} ({}人)",
members.join(", "),
members.len())
},
TaskAssignment::Department { name, manager } => {
format!("指派給{}部門,負責人: {}", name, manager)
},
}
}
fn analyze_task_urgency(priority: TaskPriority, status: TaskStatus) -> String {
match (priority, status) {
// 高優先順序且待辦的任務
(TaskPriority::High, TaskStatus::Pending) => {
String::from("緊急!需要立即處理")
},
// 自訂優先順序超過 50 且進行中
(TaskPriority::Custom(level), TaskStatus::InProgress) if level > 50 => {
String::from("超高優先順序任務進行中")
},
// 任何已完成的任務
(_, TaskStatus::Completed) => {
String::from("任務已順利完成")
},
// 已取消的高優先順序任務
(TaskPriority::High, TaskStatus::Cancelled) => {
String::from("高優先順序任務被取消,需要檢視原因")
},
// 其他所有情況
_ => String::from("任務狀態正常"),
}
}
fn find_task_by_id(tasks: &[Task], id: u32) -> Option<&Task> {
tasks.iter().find(|task| task.id == id)
}
fn main() {
let tasks = vec![
Task::new(1, String::from("學習 Rust")),
Task::new(2, String::from("寫程式")),
];
// 使用 match 處理 Option
match find_task_by_id(&tasks, 1) {
Some(task) => println!("找到任務: {}", task.title),
None => println!("找不到任務"),
}
// 使用 if let 簡化
if let Some(task) = find_task_by_id(&tasks, 2) {
println!("任務標題: {}", task.title);
}
// 使用方法鏈接
let task_title = find_task_by_id(&tasks, 1)
.map(|task| task.title.clone())
.unwrap_or_else(|| String::from("未知任務"));
println!("任務標題: {}", task_title);
}
#[derive(Debug)]
enum TaskError {
NotFound,
AlreadyCompleted,
InvalidStatus,
}
impl std::fmt::Display for TaskError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TaskError::NotFound => write!(f, "找不到任務"),
TaskError::AlreadyCompleted => write!(f, "任務已經完成"),
TaskError::InvalidStatus => write!(f, "無效的任務狀態"),
}
}
}
impl std::error::Error for TaskError {}
struct TaskManager {
tasks: Vec<Task>,
}
impl TaskManager {
fn complete_task(&mut self, id: u32) -> Result<(), TaskError> {
match self.tasks.iter_mut().find(|task| task.id == id) {
Some(task) => {
if task.is_completed {
Err(TaskError::AlreadyCompleted)
} else {
task.is_completed = true;
Ok(())
}
},
None => Err(TaskError::NotFound),
}
}
fn get_task(&self, id: u32) -> Result<&Task, TaskError> {
self.tasks.iter()
.find(|task| task.id == id)
.ok_or(TaskError::NotFound)
}
}
fn main() {
let mut manager = TaskManager {
tasks: vec![
Task::new(1, String::from("學習列舉")),
],
};
// 使用 match 處理 Result
match manager.complete_task(1) {
Ok(()) => println!("任務完成成功"),
Err(e) => println!("錯誤: {}", e),
}
// 再次嘗試完成同一任務
if let Err(e) = manager.complete_task(1) {
println!("無法完成任務: {}", e);
}
}
讓我們建立一個使用列舉的完整任務管理系統:
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
enum Priority {
Low,
Medium,
High,
Custom(u32),
}
impl Priority {
fn score(&self) -> u32 {
match self {
Priority::Low => 1,
Priority::Medium => 5,
Priority::High => 10,
Priority::Custom(score) => *score,
}
}
}
impl fmt::Display for Priority {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Priority::Low => write!(f, "低"),
Priority::Medium => write!(f, "中"),
Priority::High => write!(f, "高"),
Priority::Custom(score) => write!(f, "自訂({})", score),
}
}
}
#[derive(Debug, Clone, PartialEq)]
enum TaskStatus {
Pending,
InProgress { started_by: String },
Completed { completed_by: String },
Cancelled { reason: String },
}
impl fmt::Display for TaskStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TaskStatus::Pending => write!(f, "待辦"),
TaskStatus::InProgress { started_by } => write!(f, "進行中 ({})", started_by),
TaskStatus::Completed { completed_by } => write!(f, "已完成 ({})", completed_by),
TaskStatus::Cancelled { reason } => write!(f, "已取消 ({})", reason),
}
}
}
#[derive(Debug)]
enum TaskOperation {
Create { title: String, description: String },
Start { user: String },
Complete { user: String },
Cancel { reason: String },
ChangePriority { new_priority: Priority },
}
#[derive(Debug)]
struct Task {
id: u32,
title: String,
description: String,
priority: Priority,
status: TaskStatus,
}
impl Task {
fn new(id: u32, title: String, description: String) -> Self {
Task {
id,
title,
description,
priority: Priority::Medium,
status: TaskStatus::Pending,
}
}
fn execute_operation(&mut self, operation: TaskOperation) -> Result<String, String> {
match operation {
TaskOperation::Create { .. } => {
Err(String::from("任務已經存在"))
},
TaskOperation::Start { user } => {
match &self.status {
TaskStatus::Pending => {
self.status = TaskStatus::InProgress { started_by: user.clone() };
Ok(format!("任務已開始,執行者: {}", user))
},
_ => Err(String::from("只有待辦任務可以開始")),
}
},
TaskOperation::Complete { user } => {
match &self.status {
TaskStatus::InProgress { .. } => {
self.status = TaskStatus::Completed { completed_by: user.clone() };
Ok(format!("任務已完成,完成者: {}", user))
},
TaskStatus::Completed { .. } => {
Err(String::from("任務已經完成"))
},
_ => Err(String::from("只有進行中的任務可以完成")),
}
},
TaskOperation::Cancel { reason } => {
match &self.status {
TaskStatus::Completed { .. } => {
Err(String::from("已完成的任務無法取消"))
},
_ => {
self.status = TaskStatus::Cancelled { reason: reason.clone() };
Ok(format!("任務已取消,原因: {}", reason))
},
}
},
TaskOperation::ChangePriority { new_priority } => {
let old_priority = self.priority.clone();
self.priority = new_priority.clone();
Ok(format!("優先順序已從 {} 改為 {}", old_priority, new_priority))
},
}
}
fn get_urgency_level(&self) -> String {
match (&self.priority, &self.status) {
(Priority::High, TaskStatus::Pending) => String::from("🔴 緊急待辦"),
(Priority::Custom(score), TaskStatus::Pending) if *score > 10 => String::from("🟠 超高優先順序"),
(Priority::High | Priority::Custom(_), TaskStatus::InProgress { .. }) => String::from("🟡 重要進行中"),
(_, TaskStatus::Completed { .. }) => String::from("✅ 已完成"),
(_, TaskStatus::Cancelled { .. }) => String::from("❌ 已取消"),
_ => String::from("⚪ 一般"),
}
}
}
impl fmt::Display for Task {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}] {} - {} - {}",
self.id,
self.title,
self.priority,
self.status)
}
}
struct TaskManager {
tasks: Vec<Task>,
next_id: u32,
}
impl TaskManager {
fn new() -> Self {
TaskManager {
tasks: Vec::new(),
next_id: 1,
}
}
fn create_task(&mut self, title: String, description: String) -> u32 {
let task = Task::new(self.next_id, title, description);
let id = task.id;
self.tasks.push(task);
self.next_id += 1;
id
}
fn execute_task_operation(&mut self, task_id: u32, operation: TaskOperation) -> Result<String, String> {
match self.tasks.iter_mut().find(|task| task.id == task_id) {
Some(task) => task.execute_operation(operation),
None => Err(String::from("找不到指定的任務")),
}
}
fn list_tasks_by_status(&self, filter_status: Option<TaskStatus>) {
let filtered_tasks: Vec<_> = match filter_status {
Some(status) => self.tasks.iter()
.filter(|task| std::mem::discriminant(&task.status) == std::mem::discriminant(&status))
.collect(),
None => self.tasks.iter().collect(),
};
if filtered_tasks.is_empty() {
println!("沒有符合條件的任務");
return;
}
for task in filtered_tasks {
println!(" {} {}", task, task.get_urgency_level());
}
}
fn get_statistics(&self) -> TaskStatistics {
let mut stats = TaskStatistics::default();
for task in &self.tasks {
match task.status {
TaskStatus::Pending => stats.pending += 1,
TaskStatus::InProgress { .. } => stats.in_progress += 1,
TaskStatus::Completed { .. } => stats.completed += 1,
TaskStatus::Cancelled { .. } => stats.cancelled += 1,
}
match task.priority {
Priority::Low => stats.low_priority += 1,
Priority::Medium => stats.medium_priority += 1,
Priority::High => stats.high_priority += 1,
Priority::Custom(_) => stats.custom_priority += 1,
}
}
stats.total = self.tasks.len();
stats
}
}
#[derive(Default)]
struct TaskStatistics {
total: usize,
pending: usize,
in_progress: usize,
completed: usize,
cancelled: usize,
low_priority: usize,
medium_priority: usize,
high_priority: usize,
custom_priority: usize,
}
impl fmt::Display for TaskStatistics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "任務統計報告")?;
writeln!(f, "============")?;
writeln!(f, "總任務數: {}", self.total)?;
writeln!(f, "狀態分佈:")?;
writeln!(f, " • 待辦: {}", self.pending)?;
writeln!(f, " • 進行中: {}", self.in_progress)?;
writeln!(f, " • 已完成: {}", self.completed)?;
writeln!(f, " • 已取消: {}", self.cancelled)?;
writeln!(f, "優先順序分佈:")?;
writeln!(f, " • 低: {}", self.low_priority)?;
writeln!(f, " • 中: {}", self.medium_priority)?;
writeln!(f, " • 高: {}", self.high_priority)?;
write!(f, " • 自訂: {}", self.custom_priority)
}
}
fn main() {
let mut manager = TaskManager::new();
// 建立任務
let task1 = manager.create_task(
String::from("學習 Rust 列舉"),
String::from("深入理解列舉和模式匹配")
);
let task2 = manager.create_task(
String::from("實作專案"),
String::from("使用列舉建立任務管理系統")
);
let task3 = manager.create_task(
String::from("寫測試"),
String::from("為任務管理系統寫單元測試")
);
// 修改優先順序
match manager.execute_task_operation(
task1,
TaskOperation::ChangePriority { new_priority: Priority::High }
) {
Ok(msg) => println!("✓ {}", msg),
Err(e) => println!("✗ {}", e),
}
// 開始任務
match manager.execute_task_operation(
task1,
TaskOperation::Start { user: String::from("張小明") }
) {
Ok(msg) => println!("✓ {}", msg),
Err(e) => println!("✗ {}", e),
}
// 完成任務
match manager.execute_task_operation(
task1,
TaskOperation::Complete { user: String::from("張小明") }
) {
Ok(msg) => println!("✓ {}", msg),
Err(e) => println!("✗ {}", e),
}
// 取消任務
match manager.execute_task_operation(
task3,
TaskOperation::Cancel { reason: String::from("優先順序調整") }
) {
Ok(msg) => println!("✓ {}", msg),
Err(e) => println!("✗ {}", e),
}
println!("\n所有任務:");
manager.list_tasks_by_status(None);
println!("\n待辦任務:");
manager.list_tasks_by_status(Some(TaskStatus::Pending));
println!("\n{}", manager.get_statistics());
}
fn categorize_task(task: &Task) -> String {
match (&task.priority, &task.status) {
(Priority::Custom(score), _) if *score > 20 => String::from("超級重要"),
(Priority::High, TaskStatus::Pending) => String::from("緊急待辦"),
(priority, TaskStatus::InProgress { started_by }) if priority.score() > 7 => {
format!("重要任務進行中,執行者: {}", started_by)
},
_ => String::from("一般任務"),
}
}
fn describe_task_detail(task: &Task) -> String {
match &task.status {
TaskStatus::InProgress { started_by: user } |
TaskStatus::Completed { completed_by: user } => {
format!("任務相關人員: {}", user)
},
TaskStatus::Cancelled { reason } if reason.len() > 10 => {
format!("詳細取消原因: {}", reason)
},
status => format!("狀態: {}", status),
}
}
fn analyze_task_combination(tasks: &[Task]) -> String {
match tasks {
[] => String::from("沒有任務"),
[single] => format!("只有一個任務: {}", single.title),
[first, rest @ ..] => {
format!("首要任務: {},其餘 {} 個任務", first.title, rest.len())
},
}
}
// if let:處理單一模式
fn process_next_task(tasks: &mut Vec<Task>) {
if let Some(mut task) = tasks.pop() {
println!("處理任務: {}", task.title);
task.status = TaskStatus::InProgress {
started_by: String::from("系統")
};
} else {
println!("沒有待處理的任務");
}
}
// while let:迭代處理
fn process_all_pending(tasks: &mut Vec<Task>) {
let mut pending_tasks: Vec<_> = tasks.iter_mut()
.filter(|task| matches!(task.status, TaskStatus::Pending))
.collect();
while let Some(task) = pending_tasks.pop() {
task.status = TaskStatus::InProgress {
started_by: String::from("批次處理")
};
println!("開始任務: {}", task.title);
}
}
fn is_active_task(task: &Task) -> bool {
matches!(task.status, TaskStatus::Pending | TaskStatus::InProgress { .. })
}
fn filter_high_priority_tasks(tasks: &[Task]) -> Vec<&Task> {
tasks.iter()
.filter(|task| matches!(task.priority, Priority::High | Priority::Custom(n) if n > &10))
.collect()
}
enum TrafficLight {
Red,
Yellow,
Green,
}
// 實作以下功能:
// - next_light(&self) -> TrafficLight
// - can_go(&self) -> bool
// - wait_time(&self) -> u32 // 秒數
// - Display trait
enum Expr {
Number(f64),
Add(Box<Expr>, Box<Expr>),
Subtract(Box<Expr>, Box<Expr>),
Multiply(Box<Expr>, Box<Expr>),
Divide(Box<Expr>, Box<Expr>),
}
// 實作 eval(&self) -> Result<f64, String> 方法
// 例如:Expr::Add(Box::new(Expr::Number(2.0)), Box::new(Expr::Number(3.0)))
// 應該回傳 Ok(5.0)
enum FileSystemItem {
File {
name: String,
size: u64,
content: String
},
Directory {
name: String,
items: Vec<FileSystemItem>
},
}
// 實作以下功能:
// - total_size(&self) -> u64
// - find_item(&self, name: &str) -> Option<&FileSystemItem>
// - list_all_files(&self) -> Vec<String> // 遞迴列出所有檔案名
// 錯誤:缺少 match arm
fn bad_status_check(status: TaskStatus) -> bool {
match status {
TaskStatus::Completed { .. } => true,
// 編譯錯誤:missing match arms
}
}
// 正確:處理所有情況
fn good_status_check(status: TaskStatus) -> bool {
match status {
TaskStatus::Completed { .. } => true,
_ => false,
}
}
fn bad_example(task: Task) {
match task {
Task { status: TaskStatus::Completed { .. }, .. } => {
// task 被部分移動
},
_ => {
// println!("{}", task.title); // 錯誤!task 已被部分移動
}
}
}
// 正確:使用參考
fn good_example(task: &Task) {
match &task.status {
TaskStatus::Completed { .. } => {
println!("任務 {} 已完成", task.title); // OK
},
_ => {
println!("任務 {} 未完成", task.title); // OK
}
}
}
// 危險:程式可能 panic
fn bad_find(tasks: &[Task], id: u32) -> &Task {
tasks.iter().find(|t| t.id == id).unwrap()
}
// 安全:返回 Option
fn good_find(tasks: &[Task], id: u32) -> Option<&Task> {
tasks.iter().find(|t| t.id == id)
}