在 Rust 程式設計中,結構體 (Struct) 是組織與封裝關聯資料的核心工具,其設計緊密圍繞著型別安全與零成本抽象。本文從最基本的實例化操作切入,說明如何創建、存取及修改結構體資料,並探討其預設不可變性如何與 mut 關鍵字協作。接著,將深入分析結構體更新語法,此機制不僅簡化了程式碼,更在底層處理所有權轉移的複雜性。最後,透過介紹元組結構體與單元結構體這兩種特殊形式,展示 Rust 如何在不同場景下提供最適切的資料抽象工具,讓開發者能以更精確、更具表達力的方式建構軟體。
軟體工程師的進階修煉:從抽象化到實戰應用的全面提升
第四章:結構體與列舉 (Structs and Enums)
創建實例 (Creating Instances)
一旦定義了結構體,就可以創建它的實例。創建實例就像在大括號內指定欄位值一樣簡單。讓我們創建一個 Player 結構體的實例:
fn main() {
let player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
println!("玩家姓名: {}, 生命值: {}, 位置: ({}, {})",
player1.name, player1.health, player1.position.0, player1.position.1);
}
在這個範例中:
- 我們創建了一個名為
player1的Player實例,其中name設置為 “Alice”,health設置為 100,position設置為 (0, 0)。 - 我們使用
String::from("Alice")為name欄位創建一個字串,因為程式語言的String類型是堆分配的。 - 你可以使用點符號 (
player1.name,player1.health等) 訪問結構體的欄位。
在程式語言中,結構體實例預設是不可變的。如果你需要修改實例,你需要將其實例聲明為 mut:
fn main() {
let mut player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
player1.health = 90; // 現在我們可以修改 `health` 欄位
println!("更新後的生命值: {}", player1.health);
}
在這裡,我們將 player1 聲明為 mut,這允許我們將 health 欄位從 100 更改為 90。
使用結構體更新語法 (Using the Struct Update Syntax)
程式語言提供了一種便捷的方式,可以基於現有結構體創建新實例。這稱為結構體更新語法,它允許你將一個結構體的欄位複製到另一個結構體中,同時更新特定欄位。
讓我們看一個範例:
fn main() {
let player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
let player2 = Player {
name: String::from("Bob"), // 更新 `name` 欄位
..player1 // 使用 `player1` 的其餘欄位
};
println!("玩家2姓名: {}, 生命值: {}, 位置: ({}, {})",
player2.name, player2.health, player2.position.0, player2.position.1);
}
在這個範例中:
- 我們創建了第二個玩家
player2,其health和position與player1相同,但name不同。 ..player1語法告訴程式語言從player1複製其餘欄位 (health和position)。
結構體更新語法要求被複製的欄位(在本例中為 health 和 position)實現 Copy 特性,或者原始實例 (player1) 在更新後不再使用,因為程式語言不允許堆分配資料(如 String)有多個所有者。
元組結構體和單元結構體 (Tuple Structs and Unit-Like Structs)
程式語言提供了兩種替代形式的結構體,它們比傳統結構體更具專業性:元組結構體和單元結構體。兩者都提供了獨特的資料結構方式,但對於某些用例來說,它們更簡單且有時更有效率。讓我們深入了解每個,看看何時以及為何會使用它們。
定義和使用元組結構體 (Defining and Using Tuple Structs)
定義元組結構體的語法與定義常規元組相似,但你使用 struct 關鍵字並為元組命名:
struct Point(i32, i32); // 具有兩個欄位的元組結構體
fn main() {
let p1 = Point(5, 10); // 創建 `Point` 的實例
// 透過索引訪問欄位
println!("點座標: ({}, {})", p1.0, p1.1);
}
在這個範例中:
Point結構體有兩個欄位,都是i32類型。這與常規元組的區別在於,Point類型為這組值提供了一個有意義的名稱。- 要訪問欄位,你使用索引 (
p1.0,p1.1),就像使用普通元組一樣。
這是一個兩個具有相同形狀的元組結構體服務於不同目的的範例:
struct Point(i32, i32);
struct Dimensions(i32, i32);
fn main() {
let p = Point(5, 10);
let dim = Dimensions(1024, 768);
println!("點: ({}, {}), 維度: {}x{}", p.0, p.1, dim.0, dim.1);
}
在這個範例中:
儘管 Point 和 Dimensions 都儲存兩個 i32 值,但它們代表著非常不同的概念。透過為它們提供不同的類型名稱,我們使程式碼更易於理解。
定義和使用單元結構體 (Defining and Using Unit-Like Structs)
定義單元結構體的語法很簡單:
struct Unit;
fn main() {
let u = Unit; // 創建 `Unit` 的實例
println!("單元結構體實例已創建。");
}
在這個範例中:
Unit結構體沒有欄位,但仍然可以像任何其他類型一樣被實例化和使用。
玄貓認為,結構體的多樣性是程式語言設計彈性的體現。從傳統結構體的資料封裝,到元組結構體的簡潔命名,再到單元結構體的標記用途,每種形式都為特定的程式設計場景提供了最佳的解決方案,讓開發者能夠以最清晰和高效的方式表達資料模型。
看圖說話:
此圖示全面展示了程式語言中結構體的實例化及其多樣性。在創建結構體實例部分,它解釋了如何定義結構體後進行實例化,並透過在大括號內指定欄位值來創建,例如**Player { name: String::from("Alice"), ... } 的範例**。同時,也說明了如何使用點符號訪問欄位,以及結構體實例預設不可變,需要 mut 聲明才可變,並透過**player1.health = 90 的範例進行演示。結構體更新語法部分則介紹了基於現有實例創建新實例的方法,透過複製欄位並更新特定欄位**,使用**..existing_instance 語法**,例如**player2 { name: String::from("Bob"), ..player1 } 的範例**,並強調了要求 Copy 特性或原始實例不再使用的條件。接著,圖示介紹了兩種特殊結構體:元組結構體的定義語法 struct Name(Type1, Type2);,它類似元組但有命名類型,並透過索引訪問欄位,以**struct Point(i32, i32); 的範例說明不同命名元組結構體表示不同概念**。最後,單元結構體部分則展示了其定義語法 struct Name;,它沒有欄位,主要用作標記或實現特性,以**struct Unit; 的範例**進行說明。這些內容共同構成了程式語言結構體靈活且強大的資料建模能力。
軟體工程師的進階修煉:從抽象化到實戰應用的全面提升
第四章:結構體與列舉 (Structs and Enums)
創建實例 (Creating Instances)
一旦定義了結構體,就可以創建它的實例。創建實例就像在大括號內指定欄位值一樣簡單。讓我們創建一個 Player 結構體的實例:
fn main() {
let player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
println!("玩家姓名: {}, 生命值: {}, 位置: ({}, {})",
player1.name, player1.health, player1.position.0, player1.position.1);
}
在這個範例中:
- 我們創建了一個名為
player1的Player實例,其中name設置為 “Alice”,health設置為 100,position設置為 (0, 0)。 - 我們使用
String::from("Alice")為name欄位創建一個字串,因為程式語言的String類型是堆分配的。 - 你可以使用點符號 (
player1.name,player1.health等) 訪問結構體的欄位。
在程式語言中,結構體實例預設是不可變的。如果你需要修改實例,你需要將其實例聲明為 mut:
fn main() {
let mut player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
player1.health = 90; // 現在我們可以修改 `health` 欄位
println!("更新後的生命值: {}", player1.health);
}
在這裡,我們將 player1 聲明為 mut,這允許我們將 health 欄位從 100 更改為 90。
使用結構體更新語法 (Using the Struct Update Syntax)
程式語言提供了一種便捷的方式,可以基於現有結構體創建新實例。這稱為結構體更新語法,它允許你將一個結構體的欄位複製到另一個結構體中,同時更新特定欄位。
讓我們看一個範例:
fn main() {
let player1 = Player {
name: String::from("Alice"),
health: 100,
position: (0, 0),
};
let player2 = Player {
name: String::from("Bob"), // 更新 `name` 欄位
..player1 // 使用 `player1` 的其餘欄位
};
println!("玩家2姓名: {}, 生命值: {}, 位置: ({}, {})",
player2.name, player2.health, player2.position.0, player2.position.1);
}
在這個範例中:
- 我們創建了第二個玩家
player2,其health和position與player1相同,但name不同。 ..player1語法告訴程式語言從player1複製其餘欄位 (health和position)。
結構體更新語法要求被複製的欄位(在本例中為 health 和 position)實現 Copy 特性,或者原始實例 (player1) 在更新後不再使用,因為程式語言不允許堆分配資料(如 String)有多個所有者。
元組結構體和單元結構體 (Tuple Structs and Unit-Like Structs)
程式語言提供了兩種替代形式的結構體,它們比傳統結構體更具專業性:元組結構體和單元結構體。兩者都提供了獨特的資料結構方式,但對於某些用例來說,它們更簡單且有時更有效率。讓我們深入了解每個,看看何時以及為何會使用它們。
定義和使用元組結構體 (Defining and Using Tuple Structs)
定義元組結構體的語法與定義常規元組相似,但你使用 struct 關鍵字並為元組命名:
struct Point(i32, i32); // 具有兩個欄位的元組結構體
fn main() {
let p1 = Point(5, 10); // 創建 `Point` 的實例
// 透過索引訪問欄位
println!("點座標: ({}, {})", p1.0, p1.1);
}
在這個範例中:
Point結構體有兩個欄位,都是i32類型。這與常規元組的區別在於,Point類型為這組值提供了一個有意義的名稱。- 要訪問欄位,你使用索引 (
p1.0,p1.1),就像使用普通元組一樣。
這是一個兩個具有相同形狀的元組結構體服務於不同目的的範例:
struct Point(i32, i32);
struct Dimensions(i32, i32);
fn main() {
let p = Point(5, 10);
let dim = Dimensions(1024, 768);
println!("點: ({}, {}), 維度: {}x{}", p.0, p.1, dim.0, dim.1);
}
在這個範例中:
儘管 Point 和 Dimensions 都儲存兩個 i32 值,但它們代表著非常不同的概念。透過為它們提供不同的類型名稱,我們使程式碼更易於理解。
定義和使用單元結構體 (Defining and Using Unit-Like Structs)
定義單元結構體的語法很簡單:
struct Unit;
fn main() {
let u = Unit; // 創建 `Unit` 的實例
println!("單元結構體實例已創建。");
}
在這個範例中:
Unit結構體沒有欄位,但仍然可以像任何其他類型一樣被實例化和使用。
玄貓認為,結構體的多樣性是程式語言設計彈性的體現。從傳統結構體的資料封裝,到元組結構體的簡潔命名,再到單元結構體的標記用途,每種形式都為特定的程式設計場景提供了最佳的解決方案,讓開發者能夠以最清晰和高效的方式表達資料模型。
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
package "程式語言結構體:實例化與多樣性" {
node "創建結構體實例" as CreatingInstances {
component "定義結構體後實例化" as DefineThenInstantiate
component "欄位值在大括號內指定" as FieldValuesInBraces
component "範例: `Player { name: String::from(\"Alice\"), ... }`" as PlayerInstanceExample
component "使用點符號訪問欄位" as DotNotationAccess
component "預設不可變,需 `mut` 聲明可變實例" as ImmutableByDefaultMutForChange
component "範例: `let mut player1 = Player { ... }; player1.health = 90;`" as MutableInstanceExample
}
node "結構體更新語法" as StructUpdateSyntax {
component "基於現有實例創建新實例" as NewInstanceFromExisting
component "複製欄位並更新特定欄位" as CopyAndUpdateFields
component "語法: `..existing_instance`" as UpdateSyntaxExample
component "範例: `player2 { name: String::from(\"Bob\"), ..player1 }`" as Player2UpdateExample
component "要求 `Copy` 特性或原始實例不再使用" as CopyTraitOrNoOriginalUse
}
node "元組結構體" as TupleStructs {
component "定義語法: `struct Name(Type1, Type2);`" as TupleStructSyntax
component "類似元組,但有命名類型" as LikeTupleButNamedType
component "透過索引訪問欄位 (e.g., `p1.0`)" as IndexAccessFields
component "範例: `struct Point(i32, i32);`" as PointTupleStructExample
component "不同命名元組結構體表示不同概念" as DifferentNamesDifferentConcepts
}
node "單元結構體" as UnitLikeStructs {
component "定義語法: `struct Name;`" as UnitStructSyntax
component "沒有欄位" as NoFields
component "可用作標記或實現特性" as UsedAsMarkerOrTrait
component "範例: `struct Unit;`" as UnitStructExample
}
CreatingInstances --> DefineThenInstantiate
CreatingInstances --> FieldValuesInBraces
CreatingInstances --> PlayerInstanceExample
CreatingInstances --> DotNotationAccess
CreatingInstances --> ImmutableByDefaultMutForChange
CreatingInstances --> MutableInstanceExample
StructUpdateSyntax --> NewInstanceFromExisting
StructUpdateSyntax --> CopyAndUpdateFields
StructUpdateSyntax --> UpdateSyntaxExample
StructUpdateSyntax --> Player2UpdateExample
StructUpdateSyntax --> CopyTraitOrNoOriginalUse
TupleStructs --> TupleStructSyntax
TupleStructs --> LikeTupleButNamedType
TupleStructs --> IndexAccessFields
TupleStructs --> PointTupleStructExample
TupleStructs --> DifferentNamesDifferentConcepts
UnitLikeStructs --> UnitStructSyntax
UnitLikeStructs --> NoFields
UnitLikeStructs --> UsedAsMarkerOrTrait
UnitLikeStructs --> UnitStructExample
CreatingInstances -[hidden]-> StructUpdateSyntax
StructUpdateSyntax -[hidden]-> TupleStructs
TupleStructs -[hidden]-> UnitLikeStructs
}
@enduml
看圖說話:
此圖示全面展示了程式語言中結構體的實例化及其多樣性。在創建結構體實例部分,它解釋了如何定義結構體後進行實例化,並透過在大括號內指定欄位值來創建,例如**Player { name: String::from("Alice"), ... } 的範例**。同時,也說明了如何使用點符號訪問欄位,以及結構體實例預設不可變,需要 mut 聲明才可變,並透過**player1.health = 90 的範例進行演示。結構體更新語法部分則介紹了基於現有實例創建新實例的方法,透過複製欄位並更新特定欄位**,使用**..existing_instance 語法**,例如**player2 { name: String::from("Bob"), ..player1 } 的範例**,並強調了要求 Copy 特性或原始實例不再使用的條件。接著,圖示介紹了兩種特殊結構體:元組結構體的定義語法 struct Name(Type1, Type2);,它類似元組但有命名類型,並透過索引訪問欄位,以**struct Point(i32, i32); 的範例說明不同命名元組結構體表示不同概念**。最後,單元結構體部分則展示了其定義語法 struct Name;,它沒有欄位,主要用作標記或實現特性,以**struct Unit; 的範例**進行說明。這些內容共同構成了程式語言結構體靈活且強大的資料建模能力。
結論
解構程式語言的資料模型設計後可以發現,其結構體的多樣性不僅是語法選擇,更是對開發者思維框架的挑戰與提升。傳統結構體確保可讀性,元組結構體展現簡潔效率,單元結構體則實現零成本的語意賦予。從「知道」到「善用」的瓶頸,在於能否因應情境,做出兼顧可維護性、效能與表達力的最佳權衡,這正是資深工程師展現深度思考之處。
隨著軟體複雜度提升,資料模型的精確性與演化能力,將成為評斷程式碼品質的核心指標。熟稔這些結構體變體,是培養架構思維的前導練習。
玄貓認為,對於追求卓越的工程師,超越語法記憶,深入體會每種資料結構的設計哲學,才是從功能實現者晉升為系統建構者的關鍵修煉。