Rust-高級特征:函數指針與閉包


函數指針

我們之前學習過向函數傳遞閉包;也可以向函數傳遞常規函數。這在我們希望傳遞已經定義的函數而不是重新定義閉包作為參數時很有用。通過函數指針允許我們使用函數作為另一個函數的參數。函數的類型是 fn (使用小寫的"f") 以免與 Fn 閉包trait相混淆。fn 被稱為 函數指針(function pointer)。指定參數為函數指針的語法類似於閉包,如示例1所示:

fn add_one(x: i32) -> i32 {
    x + 1
}

fn add_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}
  //調用 let answer
= add_twice(add_one, 5); println!("The answer is {}", answer);

示例1:使用fn類型接受函數指針作為參數

這會打印出 The anser is 12。do_twice中的 f 被指定為一個接受一個i32參數並返回i32fn。接着就可以在do_twice函數體中調用f。在main中,可以將函數名add_one作為第一個參數傳遞給do_twice。 

不同於閉包,fn是一個類型而不是一個trait,所以直接指定fn作為參數而不是聲明一個帶有Fn作為trait bound的泛型參數。

函數指針實現了所有三個閉包的trait (Fn、FnMut和FnOnce),所以總是可以在調用期望閉包的函數時傳遞函數指針作為參數。傾向於編寫使用泛型和閉包trait的函數,這樣它就能接受函數或閉包作為參數。

作為一個既可以使用內聯定義的閉包又可以使用命名函數的例子,讓我們看看一個map的應用。使用map函數將一個數字vector轉換為一個字符串vector,就可以使用閉包,比如這樣:

    let list_of_numbers = vec![1,2,3];
    let list_of_strings :Vec<String> = list_of_numbers
        .iter()
        .map(|i| i.to_string())
        .collect();

或者可以將函數作為map的參數來代替閉包,像這樣:

    let list_of_numbers = vec![1,2,3];
    let list_of_strings :Vec<String> = list_of_numbers
        .iter()
        .map(ToString::to_string)
        .collect();

返回閉包

閉包表現為trait,這意味首不能直接返回閉包。對於大部分需要返回trait的情況,可以使用實現了期望返回trait的具體類型來替代函數的返回值。但是這不能用於閉包,因為他們沒有一個可返回的具體類型;例如不允許使用函數指針fn作為返回值類型。

以下代碼嘗試直接返回閉包,它並不能編譯:

fn return_closure() -> Fn(i32) -> i32 {
    |x| x+1
}

編譯器給出的錯誤是:

error[E0746]: return type cannot have an unboxed trait object
   --> src/main.rs:144:24
    |
144 | fn return_closure() -> Fn(i32) -> i32 {
    |                        ^^^^^^^^^^^^^^ doesn't have a size known at compile-time

錯誤又一次指向了Sized trait! Rust並不知道需要多少空間來儲存閉包。不過我們在之前了解到這種情況的解決方法:可以使用trait對象:

fn return_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x+1)
}

這段代碼就可以通過編譯。

深入學習


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM