Rust 閉包類型 FnMut -- FnOnce

在這篇短文中,將討論如何使用 FnMut 和 FnOnce 類型的閉包,以及它們之間的區別,並展示一些代碼示例。

首先,閉包的獨特屬性是它能在其聲明的範圍內捕獲上下文環境的變量,而不像在 rust 中的常規函數。閉包以何種方式捕獲環境變量,是由閉包的類型決定的。如 Fn,FnMut 和 FnOnce,Fn 是以不可變的方式捕捉上下文的環境變量,比較簡單。我們在文中討論其他兩個。

FnMut

這個閉包類型可以被調用多次,它以可變的方式捕捉局部變量。重要的是,你不能將可變的值移出閉包,特別是那些沒有實現 Copy trait 的類型。在示例代碼中,我們改變並訪問在閉包外聲明的 Vec,插入一個值作爲參數給閉包,同樣的例子,如果我們將閉包類型從 FnMut 更改爲 Fn(它不可變地借用了上下文變量),編譯器將立即拋出一個警告,不能可變地捕獲上下文變量。

fn take_closure<F>(mut clo: F) 
where F: FnMut(u32)
{
    clo(12);
    clo(13);
}

fn main() {
    let mut v = vec![1];
    take_closure(|n| v.push(n));
    println!("{:?}", v);   // [1, 12, 13]
}

FnOnce

FnOnce 類型捕獲上下文環境中變量值的所有權,因爲變量值的所有權已經轉移到閉包中了,所以 FnOnce 類型只能被調用一次,而 Fn 和 FnMut 類型可以調用多次。

修改上面的代碼示例,使用上面提到的 FnOnce 類型從閉包返回 vector,由於我們不能第二次調用閉包,因此拋出錯誤。

fn take_closure<F>(clo: F) 
where F: FnOnce(u32) -> Vec<u32>
{
    let vec = clo(12);
    println!("modified vector is {:?}", vec);

    clo(13);
}

fn main() {
    let mut v = vec![1];
    take_closure(|n| {
        v.push(n);
        v
    });
    println!("{:?}", v);
}

編譯錯誤:提示使用了移動後的值。

error[E0382]: use of moved value: `clo`
  --> src/main.rs:14:5
   |
8  | fn take_closure<F>(clo: F) 
   |                    --- move occurs because `clo` has type `F`, which does not implement the `Copy` trait
...
11 |     let vec = clo(12);
   |               ------- `clo` moved due to this call
...
14 |     clo(13);
   |     ^^^ value used here after move

本文翻譯自:

https://medium.com/@raviraj.dj/rusty-shorts-fnmut-fnonce-closures-7b0b8e4defd6

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

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