結構體(Struct)#
結構體(Struct) 是將多個相關的值組合成一個自訂型別的機制,類似其他語言中的「物件」或「記錄」。
1. 定義結構體#
使用 struct 關鍵字定義,每個欄位(field)都要標注型別:
1
2
3
4
5
6
| struct User {
username: String,
email: String,
age: u32,
active: bool,
}
|
2. 建立實例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| struct User {
username: String,
email: String,
age: u32,
active: bool,
}
fn main() {
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: 30,
active: true,
};
println!("用戶:{}", user.username);
println!("信箱:{}", user.email);
}
|
若要修改欄位,實例本身必須是 mut:
1
2
| let mut user = User { /* ... */ };
user.age = 31;
|
3. 欄位初始化簡寫#
當變數名稱與欄位名稱相同時,可以省略重複:
1
2
3
4
5
6
7
8
| fn new_user(username: String, email: String) -> User {
User {
username, // 等同於 username: username
email,
age: 0,
active: true,
}
}
|
4. 結構體更新語法#
基於現有實例建立新實例時,可以用 .. 繼承未明確設定的欄位:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| struct User {
username: String,
email: String,
age: u32,
active: bool,
}
fn main() {
let user1 = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: 30,
active: true,
};
let user2 = User {
email: String::from("bob@example.com"),
..user1 // 其餘欄位從 user1 取得
};
println!("user2 信箱:{}", user2.email);
println!("user2 age:{}", user2.age);
}
|
注意:..user1 會移動 user1 中 String 型別的欄位所有權。
5. 元組結構體(Tuple Struct)#
元組結構體有結構體名稱但欄位沒有名稱,透過索引存取:
1
2
3
4
5
6
7
8
9
10
| struct Point(f64, f64);
struct Color(u8, u8, u8);
fn main() {
let origin = Point(0.0, 0.0);
let red = Color(255, 0, 0);
println!("x = {}, y = {}", origin.0, origin.1);
println!("R = {}", red.0);
}
|
6. 方法(Methods)#
使用 impl 區塊為結構體定義方法。第一個參數決定如何存取 self:
| 形式 | 意義 | 使用時機 |
|---|
&self | 不可變借用 | 只讀取資料,最常見 |
&mut self | 可變借用 | 需要修改欄位 |
self | 取得所有權 | 消耗實例,呼叫後該變數失效 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn area(&self) -> f64 { // 不可變借用
self.width * self.height
}
fn scale(&mut self, factor: f64) { // 可變借用
self.width *= factor;
self.height *= factor;
}
fn into_square(self) -> Rectangle { // 取得所有權
let side = (self.width + self.height) / 2.0;
Rectangle { width: side, height: side }
}
}
fn main() {
let mut rect = Rectangle { width: 5.0, height: 3.0 };
println!("面積:{}", rect.area());
rect.scale(2.0);
let sq = rect.into_square(); // rect 的所有權移入,之後 rect 失效
println!("正方形邊長:{}", sq.width);
}
|
self(取得所有權)常見於 消耗型轉換 (into_xxx() 系列)和 Builder 模式 。Builder 模式中,每個設定方法取得並回傳 self,讓呼叫可以鏈式串接:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| struct QueryBuilder {
table: String,
limit: usize,
}
impl QueryBuilder {
fn new(table: &str) -> Self {
QueryBuilder { table: table.to_string(), limit: 100 }
}
fn limit(mut self, n: usize) -> Self {
self.limit = n;
self
}
fn build(self) -> String {
format!("SELECT * FROM {} LIMIT {}", self.table, self.limit)
}
}
fn main() {
let query = QueryBuilder::new("users")
.limit(50)
.build();
println!("{query}");
}
|
7. 關聯函數(Associated Functions)#
impl 中沒有 self 參數的函數稱為 關聯函數 ,常用來實作建構子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn new(width: f64, height: f64) -> Rectangle {
Rectangle { width, height }
}
fn square(size: f64) -> Rectangle {
Rectangle { width: size, height: size }
}
}
fn main() {
let rect = Rectangle::new(4.0, 6.0);
let sq = Rectangle::square(3.0);
println!("rect: {}x{}", rect.width, rect.height);
println!("sq: {}x{}", sq.width, sq.height);
}
|
關聯函數用 :: 呼叫,而非 .。
8. Debug 輸出#
為結構體加上 #[derive(Debug)] 屬性,就能用 {:?} 或 {:#?} 列印:
1
2
3
4
5
6
7
8
9
10
11
| #[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 1.5, y: 2.5 };
println!("{:?}", p); // Point { x: 1.5, y: 2.5 }
println!("{:#?}", p); // 格式化輸出
}
|
Reference#
https://doc.rust-lang.org/book/ch05-01-defining-structs.html