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}

閉包從它們定義的範圍捕獲變量和函數。有三種形式:

讓我們嘗試從一個函數返回閉包,並將其傳遞給另一個函數:

 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 中遇到閉包,例如:

本文翻譯自:

https://medium.com/@the.makcym/rust-understanding-closures-d037fb5c8177

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/FU8KItutU4cCG-UGs7PayQ