Rust基礎筆記:閉包

語法

Closure看上去是這樣的:html

let plus_one = |x: i32| x + 1;
    assert_eq!(2, plus_one(1));

首先建立一個綁定plus_one,而後將它分配給一個closure,body是一個expression,注意{ } 也是一個expression。express

它也能夠被寫成這樣:閉包

let plus_two = |x| {
        let mut result: i32 = x;
    
        result += 1;
        result += 1;
    
        result
    };
    assert_eq!(4, plus_two(2));

和常規的函數定義相比,區別就是closure沒有使用關鍵詞 fn ,區分一下:函數

fn  plus_one_v1   (x: i32) -> i32 { x + 1 }
let plus_one_v2 = |x: i32| -> i32 { x + 1 };
let plus_one_v3 = |x: i32|          x + 1  ;

值得注意的是在closure中參數和返回值的類型都是能夠省略的,下面這種形式也是能夠的:code

let plus_one = |x| x + 1;

閉包和它的環境

一個小例子:htm

let num = 5;
    let plus_num = |x: i32| x + num;
    
    assert_eq!(10, plus_num(5));

也就是說,plus_num引用了一個在它做用於中的變量num,具體地說這是一個borrow,它知足全部權系統的要求,來看一個錯誤的例子:資源

let mut num = 5;
let plus_num = |x: i32| x + num;

let y = &mut num;

error: cannot borrow `num` as mutable because it is also borrowed as immutable
    let y = &mut num;
                 ^~~

在上面的代碼中,plus_num已經對num作了不可變引用,而在plus_one的做用域內,又發生了一次可變引用,因此就違反了全部權系統中的以下規則:作用域

若是對一個綁定進行了不可變引用,那麼在該引用未超出做用域以前,不能夠再進行可變引用,反之也是同樣。get

對代碼作出以下修改便可:編譯器

let mut num = 5;
    {
        let plus_num = |x: i32| x + num;
    
    } // plus_num goes out of scope, borrow of num ends
    
    let y = &mut num;

再看一個例子:

let nums = vec![1, 2, 3];
    let takes_nums = || nums;
    println!("{:?}", nums);

有問題嗎?
有,並且是大問題,編譯器的報錯以下:

closure.rs:8:19: 8:23 error: use of moved value: `nums` [E0382]
closure.rs:8    println!("{:?}", nums);

從錯誤中能夠看出來,在最後一個輸出語句中,nums已經沒有對資源 vec![1, 2, 3] 的 全部權了,該資源的全部權已經被move到了closure中去了。

那麼問題來了:

爲何在前面的例子中closure是borrow,而到了這裏就變成了move了呢?

咱們從頭梳理一遍:

let mut num = 5;
    let plus_num = || num + 1;
    let num2 = &mut num;
Error:
closure.rs:5:21: 5:24 error: cannot borrow `num` as mutable because it is also borrowed as immutable
closure.rs:5     let num2 = &mut num;

說明在closure中發生了immutable borrow,這樣纔會和下面的&mut衝突,如今咱們來作一個改動:

let plus_num = || num + 1; 
    // 改爲以下語句
    let mut plue_num = || num += 1;

再編譯一次:

Error:
closure.rs:4:17: 4:20 error: cannot borrow `num` as mutable more than once at a time
closure.rs:4 let num2 = &mut num;

能夠發現,在closure中發生了mutable borrow,爲何會這樣呢?

在closure無非就是這3種狀況:

  • by reference: &T

  • by mutable reference: &mut T

  • by value: T

    至因而這3箇中的哪個,取決於你closure內部怎麼用,而後編譯器自動推斷綁定的類型是Fn() FnMut() 仍是FnOnce()

let plus_num = || num + 1;         // 這個只須要引用便可,因此plus_num類型爲Fn()
    let mut plue_num = || num += 1;    // 這個則須要&mut T,因此plus_num類型爲FnMut()
    // 這是手冊裏的一個例子
    // 這是一個沒有實現Copy trait的類型
    let movable = Box::new(3);
    // `drop` 須要類型T,因此closure環境就須要 by value T.,因此consume類型爲FnOnce()
    let consume = || {
        drop(movable);    // 這裏發生了move
    };
    // 因此這個consume只能執行一次
    consume();

有一點要注意的是:
在前面的例子應該分紅兩類:

  1. let a= 100i32;

  2. let a = vec![1,2,3];

區別就是i32類型實現了copy trait,而vector沒有!!!

參考:http://rustbyexample.com/fn/closures/capture.html

Move closure

使用move關鍵字,強制closure得到全部權,但下面的例子得注意一下:

let num = 5;
    let owns_num = move |x: i32| x + num;

儘管這裏使用move,變量遵循move語義,可是,在這裏5實現了Copy,因此owns_own得到的是 5 的拷貝的全部權,有什麼區別呢?
來看看這段代碼:

let mut num = 5;
    {
        let mut add_num = |x: i32| num += x;
        add_num(5);
    }
    assert_eq!(10, num);

這段代碼獲得的是咱們想要的結果,可是若是咱們加上move關鍵字呢?上面的代碼就會報錯,由於num的值還是 5 ,並無發生改變,

爲何呢?
上面說到了,move強制閉包環境得到全部權,可是 5 實現了Copy,因此閉包得到的是其拷貝的全部權,同理閉包中修改的也是 5 的拷貝。

總結

在Rust中閉包的概念並很差理解,由於牽扯到了太多全部權的概念,能夠先把全部權弄懂了,閉包也就好理解了。

相關文章
相關標籤/搜索