函數(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
}

一般函數做不到這件事——函數只能使用自己的參數和內部定義的變數。

常見用途:迭代器#

閉包最常見的場景是配合迭代器方法(mapfilterfor_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