函數(Function)#
函數是將一段具有特定功能的程式碼命名後,可以重複呼叫的機制。
Rust 使用 fn 關鍵字定義函數,命名慣例為 蛇形命名法(snake_case) 。
1. 定義與呼叫函數#
1
2
3
4
5
6
7
| fn greet() {
println!("你好,Rust!");
}
fn main() {
greet(); // 呼叫函數
}
|
Rust 不在意函數定義的順序,只要在同一個作用域內都可以呼叫。
2. 參數(Parameters)#
函數參數 必須明確標注型別 :
1
2
3
4
5
6
7
| fn add(x: i32, y: i32) {
println!("{x} + {y} = {}", x + y);
}
fn main() {
add(3, 5); // 3 + 5 = 8
}
|
3. 回傳值(Return Value)#
使用 -> 標注回傳型別。Rust 函數的回傳值是 最後一個表達式的值 (不加分號):
1
2
3
4
5
6
7
8
| fn square(n: i32) -> i32 {
n * n // 不加分號,自動回傳
}
fn main() {
let result = square(4);
println!("4 的平方是 {result}"); // 16
}
|
也可以用 return 提前回傳:
1
2
3
4
5
6
| fn is_even(n: i32) -> bool {
if n % 2 == 0 {
return true;
}
false
}
|
4. 陳述式與表達式#
Rust 區分 陳述式(Statement) 和 表達式(Expression) :
- 陳述式 :執行某動作, 不回傳值 ,以
; 結尾。 - 表達式 :計算並 回傳值 , 不加
; 。
1
2
3
4
5
6
7
| fn main() {
let y = {
let x = 3;
x + 1 // 表達式,回傳 4
};
println!("y = {y}"); // y = 4
}
|
5. 多個回傳值(透過元組)#
Rust 函數只能有一個回傳值,但可以用元組回傳多個值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| fn min_max(arr: &[i32]) -> (i32, i32) {
let mut min = arr[0];
let mut max = arr[0];
for &val in arr {
if val < min { min = val; }
if val > max { max = val; }
}
(min, max)
}
fn main() {
let numbers = [3, 1, 4, 1, 5, 9, 2, 6];
let (min, max) = min_max(&numbers);
println!("最小值:{min},最大值:{max}");
}
|
6. 閉包(Closure)#
閉包是一種 匿名函數 ,可以直接寫在程式碼中間,不需要另外取名字。
語法是用兩個 | 包住參數列表,後面接函數主體:
|參數| 主體
1
2
3
4
5
6
7
8
9
| fn main() {
// 一般函數:需要取名、另外定義
fn double(x: i32) -> i32 { x * 2 }
// 閉包:直接寫在這裡,存入變數
let double = |x| x * 2;
println!("{}", double(5)); // 10
}
|
語法變化#
閉包的主體如果只有一個表達式,可以直接寫;如果有多行,用 {} 包起來:
1
2
3
4
5
6
7
8
9
10
| fn main() {
let add = |x, y| x + y; // 單一表達式,自動回傳
let greet = |name| { // 多行用大括號
let msg = format!("你好,{name}!");
println!("{msg}");
};
println!("{}", add(3, 4)); // 7
greet("Alice"); // 你好,Alice!
}
|
閉包的型別通常可以自動推斷,不需要標注;若需要明確標注,寫法和函數相同:
1
| let multiply = |x: i32, y: i32| -> i32 { x * y };
|
捕獲環境變數#
閉包最重要的特性:它可以直接使用 定義時所在範圍內的變數 ,不需要透過參數傳入。這稱為「捕獲(capture)」:
1
2
3
4
5
6
7
8
| fn main() {
let base = 10;
let add_base = |n| n + base; // base 不是參數,但閉包可以直接使用它
println!("{}", add_base(5)); // 15
println!("{}", add_base(20)); // 30
}
|
一般函數做不到這件事——函數只能使用自己的參數和內部定義的變數。
常見用途:迭代器#
閉包最常見的場景是配合迭代器方法(map、filter、for_each 等),以簡潔的方式處理集合(Vec 集合見 CH13,迭代器見 CH16):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// map:對每個元素套用閉包,產生新集合
let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
println!("{:?}", doubled); // [2, 4, 6, 8, 10]
// filter:保留讓閉包回傳 true 的元素
let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
println!("{:?}", evens); // [2, 4]
// for_each:對每個元素執行閉包(不產生新集合)
numbers.iter().for_each(|&x| print!("{x} ")); // 1 2 3 4 5
}
|
與函數的差異#
| 函數 | 閉包 |
|---|
| 名稱 | 必須有名稱 | 匿名 |
| 捕獲環境變數 | 不行 | 可以 |
| 型別標注 | 必須 | 可省略 |
| 定義位置 | 頂層或 impl 內 | 任意位置 |
7. 函數作為參數#
Rust 支援將函數當作參數傳遞:
1
2
3
4
5
6
7
8
9
10
11
12
| fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn double(n: i32) -> i32 {
n * 2
}
fn main() {
let result = apply(double, 5);
println!("{result}"); // 10
}
|
Reference#
https://doc.rust-lang.org/book/ch03-03-how-functions-work.html