Rust 閉包
Rust 是一種多範例語言,因此它支持純函數式編程 (FP) 語言的一些特性,其中就包括閉包。
在函數式編程語言中,如 Haskell、Clojure、Erlang 等,函數是最重要的部分,你可以將函數賦值給變量,將函數作爲參數傳遞給其他函數,從其他函數返回。爲了在 Rust 中提供同樣的可能性,添加了閉包。
閉包的另一個術語是匿名函數,這是因爲函數有名稱,例如 main,但閉包沒有。讓我們定義簡單閉包:
let simple_closure = || ();
simple_closure();
這個閉包完全沒做任何事情:它不接受任何參數,返回 unit ()。我們可以看到閉包的調用類似於函數,我們還可以將閉包賦給變量。讓我們來看看更有趣的例子:
let add = |a, b| a + b;
println!("{}", add(1, 2)); // stdout: 3
閉包定義中的 “|” 與函數定義中的括號具有類似的作用。
在這裏我們還可以看到與函數的另一個區別:Rust 編譯器可以在不明確指定參數類型的情況下推斷參數類型,因此可以忽略它們。這在大多數情況下是正確的,但有時編譯器可能會要求你顯式地標註類型。
一旦編譯器推斷出參數類型,就不允許更改它。以下代碼無法編譯:
let closure = |x| x;
closure(3); // compiler infer int type
closure("hello from rusty closure"); // Error!
編譯器在第三行期望參數爲整數類型,但發現 & str,所以出現類型不匹配錯誤。
你還應該記得所有權原則,看看下面的錯誤代碼示例:
1#[derive(Debug)]
2struct Point {
3 x: isize,
4 y: isize,
5}
6
7fn main() {
8 let point = Point { x: 6, y: 8 };
9 let dist = |point: Point| {
10 let squared_dist = point.x.pow(2) + point.y.pow(2);
11 let squared_dist = squared_dist as f32;
12 squared_dist.powf(0.5)
13 };
14 println!("{}", dist(point)); // Ok
15 println!("{:?}", point); // Error!
16}
首先,這個閉包有多行語句,所以它們出現在大括號中。其次,編譯器不能推斷參數類型,所以必須顯式添加類型。
這段代碼無法編譯,這是因爲 closure 成爲 Point 參數的所有者,最後一行試圖訪問移動後的 Point 值,因此會導致程序失敗。
閉包最重要的一點是,閉包可以捕獲上下文環境的變量。這是什麼意思?讓我們考慮簡單的代碼片段:
1fn main() {
2 let a = 5;
3 let c = || println!("{}", a);
4 {
5 let a = 10;
6 c(); // stdout: 5
7 }
8}
閉包從它們定義的範圍捕獲變量和函數。有三種形式:
-
閉包只讀取環境中的變量。這種閉包通過引用訪問變量並實現 Fn 特徵。
-
閉包改變環境中的變量。這種閉包捕獲可變引用並實現 FnMut 特徵。
-
如果閉包擁有沒有實現 Copy trait 的變量,這種閉包實現了 FnOnce 特徵,並且只能被調用一次。
讓我們嘗試從一個函數返回閉包,並將其傳遞給另一個函數:
1fn returns_closure() -> impl Fn() -> () {
2 || println!("Hello from closure")
3}
4
5fn takes_closure<F>(closure: F)
6where F: Fn()
7{
8 closure();
9}
10
11fn main() {
12 let closure = returns_closure();
13 takes_closure(closure);
14}
你可能會在 std 中遇到閉包,例如:
-
由 Result enum 實現的 unwrap_or_else(closure) 方法,接受閉包並在 Result 匹配 Err(_) 時運行它。
-
Tread::spawn(closure) 函數,接受閉包並在新線程中運行。
-
由 iter::Iterator 實現的 filter(closure) 方法接受閉包,返回 bool 值,表示集合中的項是否會被選中。
本文翻譯自:
https://medium.com/@the.makcym/rust-understanding-closures-d037fb5c8177
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/FU8KItutU4cCG-UGs7PayQ