W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
ch19-05-advanced-functions-and-closures.md
commit 9e30688e0ac4a1ad86fc60aa380bebfb1c34b8a7
接下來(lái)我們將探索一些有關(guān)函數(shù)和閉包的高級(jí)功能:函數(shù)指針以及返回值閉包。
我們討論過(guò)了如何向函數(shù)傳遞閉包;也可以向函數(shù)傳遞常規(guī)函數(shù)!這在我們希望傳遞已經(jīng)定義的函數(shù)而不是重新定義閉包作為參數(shù)時(shí)很有用。通過(guò)函數(shù)指針允許我們使用函數(shù)作為另一個(gè)函數(shù)的參數(shù)。函數(shù)的類(lèi)型是 fn
(使用小寫(xiě)的 “f” )以免與 Fn
閉包 trait 相混淆。fn
被稱(chēng)為 函數(shù)指針(function pointer)。指定參數(shù)為函數(shù)指針的語(yǔ)法類(lèi)似于閉包,如示例 19-27 所示:
文件名: src/main.rs
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
}
示例 19-27: 使用 fn
類(lèi)型接受函數(shù)指針作為參數(shù)
這會(huì)打印出 The answer is: 12
。do_twice
中的 f
被指定為一個(gè)接受一個(gè) i32
參數(shù)并返回 i32
的 fn
。接著就可以在 do_twice
函數(shù)體中調(diào)用 f
。在 main
中,可以將函數(shù)名 add_one
作為第一個(gè)參數(shù)傳遞給 do_twice
。
不同于閉包,fn
是一個(gè)類(lèi)型而不是一個(gè) trait,所以直接指定 fn
作為參數(shù)而不是聲明一個(gè)帶有 Fn
作為 trait bound 的泛型參數(shù)。
函數(shù)指針實(shí)現(xiàn)了所有三個(gè)閉包 trait(Fn
、FnMut
和 FnOnce
),所以總是可以在調(diào)用期望閉包的函數(shù)時(shí)傳遞函數(shù)指針作為參數(shù)。傾向于編寫(xiě)使用泛型和閉包 trait 的函數(shù),這樣它就能接受函數(shù)或閉包作為參數(shù)。
一個(gè)只期望接受 fn
而不接受閉包的情況的例子是與不存在閉包的外部代碼交互時(shí):C 語(yǔ)言的函數(shù)可以接受函數(shù)作為參數(shù),但 C 語(yǔ)言沒(méi)有閉包。
作為一個(gè)既可以使用內(nèi)聯(lián)定義的閉包又可以使用命名函數(shù)的例子,讓我們看看一個(gè) map
的應(yīng)用。使用 map
函數(shù)將一個(gè)數(shù)字 vector 轉(zhuǎn)換為一個(gè)字符串 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();
或者可以將函數(shù)作為 map
的參數(shù)來(lái)代替閉包,像是這樣:
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(ToString::to_string).collect();
注意這里必須使用 “高級(jí) trait” 部分講到的完全限定語(yǔ)法,因?yàn)榇嬖诙鄠€(gè)叫做 to_string
的函數(shù);這里使用了定義于 ToString
trait 的 to_string
函數(shù),標(biāo)準(zhǔn)庫(kù)為所有實(shí)現(xiàn)了 Display
的類(lèi)型實(shí)現(xiàn)了這個(gè) trait。
另一個(gè)實(shí)用的模式暴露了元組結(jié)構(gòu)體和元組結(jié)構(gòu)體枚舉成員的實(shí)現(xiàn)細(xì)節(jié)。這些項(xiàng)使用 ()
作為初始化語(yǔ)法,這看起來(lái)就像函數(shù)調(diào)用,同時(shí)它們確實(shí)被實(shí)現(xiàn)為返回由參數(shù)構(gòu)造的實(shí)例的函數(shù)。它們也被稱(chēng)為實(shí)現(xiàn)了閉包 trait 的函數(shù)指針,并可以采用類(lèi)似如下的方式調(diào)用:
enum Status {
Value(u32),
Stop,
}
let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
這里創(chuàng)建了 Status::Value
實(shí)例,它通過(guò) map
用范圍的每一個(gè) u32
值調(diào)用 Status::Value
的初始化函數(shù)。一些人傾向于函數(shù)風(fēng)格,一些人喜歡閉包。這兩種形式最終都會(huì)產(chǎn)生同樣的代碼,所以請(qǐng)使用對(duì)你來(lái)說(shuō)更明白的形式吧。
閉包表現(xiàn)為 trait,這意味著不能直接返回閉包。對(duì)于大部分需要返回 trait 的情況,可以使用實(shí)現(xiàn)了期望返回的 trait 的具體類(lèi)型來(lái)替代函數(shù)的返回值。但是這不能用于閉包,因?yàn)樗麄儧](méi)有一個(gè)可返回的具體類(lèi)型;例如不允許使用函數(shù)指針 fn
作為返回值類(lèi)型。
這段代碼嘗試直接返回閉包,它并不能編譯:
fn returns_closure() -> dyn Fn(i32) -> i32 {
|x| x + 1
}
編譯器給出的錯(cuò)誤是:
$ cargo build
Compiling functions-example v0.1.0 (file:///projects/functions-example)
error[E0746]: return type cannot have an unboxed trait object
--> src/lib.rs:1:25
|
1 | fn returns_closure() -> dyn Fn(i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
help: use `impl Fn(i32) -> i32` as the return type, as all return paths are of type `[closure@src/lib.rs:2:5: 2:14]`, which implements `Fn(i32) -> i32`
|
1 | fn returns_closure() -> impl Fn(i32) -> i32 {
| ~~~~~~~~~~~~~~~~~~~
For more information about this error, try `rustc --explain E0746`.
error: could not compile `functions-example` due to previous error
錯(cuò)誤又一次指向了 Sized
trait!Rust 并不知道需要多少空間來(lái)儲(chǔ)存閉包。不過(guò)我們?cè)谏弦徊糠忠?jiàn)過(guò)這種情況的解決辦法:可以使用 trait 對(duì)象:
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
這段代碼正好可以編譯。關(guān)于 trait 對(duì)象的更多內(nèi)容,請(qǐng)回顧第十七章的 “為使用不同類(lèi)型的值而設(shè)計(jì)的 trait 對(duì)象” 部分。
接下來(lái)讓我們學(xué)習(xí)宏!
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: