Hi 大家,現在我們已經掌握了 Rust 的核心概念:所有權、借用和生命週期。今天我們來看看如何組織和構建更複雜的資料結構 - 結構體(Structs)。
假設你想表示一個使用者的資料:
// 使用個別變數(不建議)
let name = String::from("張小明");
let email = String::from("ming@example.com");
let age = 25;
let is_active = true;
// 使用元組(稍好但不夠清楚)
let user = (String::from("張小明"), String::from("ming@example.com"), 25, true);
let email = user.1; // 不直觀,容易出錯
結構體提供了更好的解決方案:
struct User {
name: String,
email: String,
age: u32,
is_active: bool,
}
let user = User {
name: String::from("張小明"),
email: String::from("ming@example.com"),
age: 25,
is_active: true,
};
let email = user.email; // 清楚明確
// 定義一個任務結構體
struct Task {
id: u32,
title: String,
description: String,
is_completed: bool,
priority: u32,
}
// 建立實例
let task = Task {
id: 1,
title: String::from("學習 Rust 結構體"),
description: String::from("理解結構體的定義和使用方法"),
is_completed: false,
priority: 1,
};
fn main() {
let mut task = Task {
id: 1,
title: String::from("學習 Rust"),
description: String::from("每日學習"),
is_completed: false,
priority: 1,
};
// 讀取欄位
println!("任務標題: {}", task.title);
println!("優先順序: {}", task.priority);
// 修改欄位(需要 mut)
task.is_completed = true;
task.priority = 2;
println!("任務狀態: {}", if task.is_completed { "已完成" } else { "未完成" });
}
當變數名和欄位名相同時,可以簡寫:
fn create_user(name: String, email: String, age: u32) -> User {
User {
name, // 等同於 name: name
email, // 等同於 email: email
age, // 等同於 age: age
is_active: true,
}
}
從其他實例建立新實例:
let user1 = User {
name: String::from("張小明"),
email: String::from("ming@example.com"),
age: 25,
is_active: true,
};
// 建立新使用者,複用大部分資料
let user2 = User {
name: String::from("李小華"),
email: String::from("hua@example.com"),
..user1 // 其他欄位使用 user1 的值
};
// 注意:user1 的 age 和 is_active 被移動到 user2
// 如果只有 Copy 型別被複用,user1 仍然可用
有欄位但沒有欄位名的結構體:
// 定義元組結構體
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// 存取欄位使用索引
println!("顏色: RGB({}, {}, {})", black.0, black.1, black.2);
// 解構賦值
let Point(x, y, z) = origin;
println!("座標: ({}, {}, {})", x, y, z);
}
沒有任何欄位的結構體:
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
// 主要用於實作特徵,稍後學習
}
使用 impl
區塊定義方法:
impl Task {
// 關聯函式(類似靜態方法)
fn new(id: u32, title: String, description: String) -> Task {
Task {
id,
title,
description,
is_completed: false,
priority: 1,
}
}
// 實例方法
fn complete(&mut self) {
self.is_completed = true;
}
fn is_high_priority(&self) -> bool {
self.priority >= 3
}
fn display(&self) {
let status = if self.is_completed { "✓" } else { "○" };
println!("{} [{}] {} - 優先順序: {}",
status, self.id, self.title, self.priority);
}
// 消費 self 的方法
fn archive(self) -> String {
format!("任務 '{}' 已歸檔", self.title)
}
}
fn main() {
// 使用關聯函式建立實例
let mut task = Task::new(
1,
String::from("學習結構體"),
String::from("掌握 Rust 結構體的用法")
);
// 調用實例方法
task.display();
if !task.is_high_priority() {
println!("設定高優先順序");
task.priority = 5;
}
task.complete();
task.display();
// 歸檔任務(消費所有權)
let message = task.archive();
println!("{}", message);
// task 已經被移動,不能再使用
// task.display(); // 錯誤!
}
建立一個完整的任務管理結構體:
use std::fmt;
#[derive(Debug, Clone)]
enum Priority {
Low = 1,
Medium = 2,
High = 3,
Urgent = 4,
}
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::Urgent => write!(f, "緊急"),
}
}
}
#[derive(Debug)]
struct Task {
id: u32,
title: String,
description: String,
is_completed: bool,
priority: Priority,
tags: Vec<String>,
}
impl Task {
fn new(id: u32, title: String, description: String) -> Self {
Task {
id,
title,
description,
is_completed: false,
priority: Priority::Medium,
tags: Vec::new(),
}
}
fn set_priority(&mut self, priority: Priority) {
self.priority = priority;
}
fn add_tag(&mut self, tag: String) {
self.tags.push(tag);
}
fn complete(&mut self) {
self.is_completed = true;
}
fn is_high_priority(&self) -> bool {
matches!(self.priority, Priority::High | Priority::Urgent)
}
fn get_summary(&self) -> String {
format!("[{}] {} - {}",
if self.is_completed { "✓" } else { "○" },
self.title,
self.priority)
}
}
impl fmt::Display for Task {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.get_summary())?;
if !self.tags.is_empty() {
write!(f, " 標籤: [{}]", self.tags.join(", "))?;
}
Ok(())
}
}
#[derive(Debug)]
struct TaskManager {
tasks: Vec<Task>,
next_id: u32,
}
impl TaskManager {
fn new() -> Self {
TaskManager {
tasks: Vec::new(),
next_id: 1,
}
}
fn add_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 get_task_mut(&mut self, id: u32) -> Option<&mut Task> {
self.tasks.iter_mut().find(|task| task.id == id)
}
fn complete_task(&mut self, id: u32) -> bool {
if let Some(task) = self.get_task_mut(id) {
task.complete();
true
} else {
false
}
}
fn list_tasks(&self) {
if self.tasks.is_empty() {
println!("沒有任務");
return;
}
println!("所有任務:");
for task in &self.tasks {
println!(" {}", task);
}
}
fn list_pending_tasks(&self) {
let pending: Vec<_> = self.tasks.iter()
.filter(|task| !task.is_completed)
.collect();
if pending.is_empty() {
println!("沒有待辦任務!");
return;
}
println!("待辦任務:");
for task in pending {
println!(" {}", task);
}
}
fn get_task_count(&self) -> (usize, usize) {
let total = self.tasks.len();
let completed = self.tasks.iter()
.filter(|task| task.is_completed)
.count();
(completed, total)
}
}
fn main() {
let mut manager = TaskManager::new();
// 新增任務
let id1 = manager.add_task(
String::from("學習 Rust 結構體"),
String::from("理解結構體的定義和使用")
);
let id2 = manager.add_task(
String::from("實作任務管理器"),
String::from("建立一個完整的任務管理系統")
);
let id3 = manager.add_task(
String::from("寫測試"),
String::from("為任務管理器寫單元測試")
);
// 設定優先順序和標籤
if let Some(task) = manager.get_task_mut(id1) {
task.set_priority(Priority::High);
task.add_tag(String::from("學習"));
task.add_tag(String::from("基礎"));
}
if let Some(task) = manager.get_task_mut(id2) {
task.set_priority(Priority::Urgent);
task.add_tag(String::from("實作"));
task.add_tag(String::from("專案"));
}
// 列出所有任務
manager.list_tasks();
println!();
// 完成一個任務
if manager.complete_task(id1) {
println!("任務 {} 已完成!", id1);
}
println!();
// 列出待辦任務
manager.list_pending_tasks();
println!();
// 顯示統計
let (completed, total) = manager.get_task_count();
println!("任務統計: {}/{} 已完成 ({:.1}%)",
completed, total,
(completed as f64 / total as f64) * 100.0);
}
fn process_task(task: Task) -> String {
// task 的所有權被轉移到這個函式
format!("處理任務: {}", task.title)
// task 在函式結束時被丟棄
}
fn main() {
let task = Task::new(1, String::from("測試"), String::from("測試所有權"));
let result = process_task(task);
println!("{}", result);
// println!("{:?}", task); // 錯誤!task 已被移動
}
fn analyze_task(task: &Task) -> String {
// 只借用 task,不取得所有權
format!("任務分析: {} - 狀態: {}",
task.title,
if task.is_completed { "已完成" } else { "進行中" })
}
fn modify_task(task: &mut Task) {
// 可變借用,可以修改
if !task.is_completed {
task.priority = Priority::High;
}
}
fn main() {
let mut task = Task::new(1, String::from("測試"), String::from("測試借用"));
let analysis = analyze_task(&task); // 不可變借用
println!("{}", analysis);
modify_task(&mut task); // 可變借用
println!("修改後: {:?}", task); // task 仍然有效
}
使用 #[derive]
自動實作常用特徵:
#[derive(Debug, Clone, PartialEq, Eq)]
struct SimpleTask {
id: u32,
title: String,
completed: bool,
}
fn main() {
let task1 = SimpleTask {
id: 1,
title: String::from("任務1"),
completed: false,
};
// Debug: 可以使用 {:?} 格式化
println!("Debug: {:?}", task1);
// Clone: 可以複製
let task2 = task1.clone();
// PartialEq: 可以比較相等性
println!("相等?: {}", task1 == task2);
// 原來的 task1 仍然有效
println!("原始任務: {:?}", task1);
}
結構體可以包含其他結構體:
#[derive(Debug)]
struct Address {
street: String,
city: String,
postal_code: String,
}
#[derive(Debug)]
struct Person {
name: String,
email: String,
address: Address,
}
impl Person {
fn new(name: String, email: String, address: Address) -> Self {
Person { name, email, address }
}
fn full_address(&self) -> String {
format!("{}, {}, {}",
self.address.street,
self.address.city,
self.address.postal_code)
}
}
fn main() {
let address = Address {
street: String::from("民生東路一段100號"),
city: String::from("台北市"),
postal_code: String::from("10491"),
};
let person = Person::new(
String::from("張小明"),
String::from("ming@example.com"),
address
);
println!("姓名: {}", person.name);
println!("地址: {}", person.full_address());
println!("完整資訊: {:#?}", person);
}
impl Task {
// 基本建構函式
fn new(id: u32, title: String) -> Self {
Task {
id,
title,
description: String::new(),
is_completed: false,
priority: Priority::Medium,
tags: Vec::new(),
}
}
// 建構器模式
fn with_description(mut self, description: String) -> Self {
self.description = description;
self
}
fn with_priority(mut self, priority: Priority) -> Self {
self.priority = priority;
self
}
fn with_tags(mut self, tags: Vec<String>) -> Self {
self.tags = tags;
self
}
}
// 使用建構器模式
let task = Task::new(1, String::from("學習 Rust"))
.with_description(String::from("深入學習 Rust 語言"))
.with_priority(Priority::High)
.with_tags(vec![
String::from("學習"),
String::from("程式設計"),
]);
impl TaskManager {
fn add_and_configure_task(&mut self, title: String) -> &mut Task {
let id = self.add_task(title, String::new());
self.get_task_mut(id).unwrap()
}
}
// 方法鏈接使用
manager.add_and_configure_task(String::from("新任務"))
.set_priority(Priority::High);
struct OptionalTask {
id: u32,
title: String,
description: Option<String>, // 可選的描述
due_date: Option<String>, // 可選的截止日期
assigned_to: Option<String>, // 可選的負責人
}
impl OptionalTask {
fn new(id: u32, title: String) -> Self {
OptionalTask {
id,
title,
description: None,
due_date: None,
assigned_to: None,
}
}
fn has_due_date(&self) -> bool {
self.due_date.is_some()
}
fn set_due_date(&mut self, date: String) {
self.due_date = Some(date);
}
}
建立一個學生結構體,包含以下功能:
struct Student {
id: u32,
name: String,
grades: Vec<f64>,
}
// 實作以下方法:
// - new(id, name) -> Student
// - add_grade(&mut self, grade: f64)
// - average_grade(&self) -> Option<f64>
// - is_passing(&self) -> bool // 平均分數 >= 60
// - get_summary(&self) -> String
struct Book {
isbn: String,
title: String,
author: String,
is_available: bool,
}
struct Library {
books: Vec<Book>,
}
// 實作以下功能:
// - Library::new() -> Library
// - add_book(&mut self, book: Book)
// - find_book(&self, isbn: &str) -> Option<&Book>
// - borrow_book(&mut self, isbn: &str) -> Result<(), String>
// - return_book(&mut self, isbn: &str) -> Result<(), String>
// - available_books(&self) -> Vec<&Book>
#[derive(Debug, Clone)]
struct Product {
id: u32,
name: String,
price: f64,
}
struct CartItem {
product: Product,
quantity: u32,
}
struct ShoppingCart {
items: Vec<CartItem>,
discount_rate: f64, // 折扣率 0.0-1.0
}
// 實作完整的購物車功能:
// - add_item, remove_item, update_quantity
// - calculate_subtotal, calculate_total
// - apply_discount, clear_cart
// - get_item_count, is_empty
mut
let user = User { /* ... */ };
user.name = String::from("新名字"); // 錯誤!需要 mut
let mut user = User { /* ... */ };
user.name = String::from("新名字"); // 正確
let user1 = User { /* ... */ };
let user2 = User {
email: String::from("new@example.com"),
..user1
};
// user1 的非 Copy 欄位被移動到 user2
println!("{}", user1.name); // 錯誤!name 已被移動
impl Task {
// 錯誤:應該是 &self 而不是 self
fn display(self) {
println!("{}", self.title);
} // self 被消費了!
}
明天我們將學習 列舉與模式匹配,這是 Rust 另一個強大的特性,讓你能夠表示「多種可能性中的一種」,並且安全地處理所有情況。我們會學習如何定義枚舉型別、使用 match
表達式,以及如何結合結構體建立更表達性的型別系統。