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();
有一點要注意的是:
在前面的例子應該分紅兩類:
let a= 100i32;
let a = vec![1,2,3];
區別就是i32類型實現了copy trait,而vector沒有!!!
使用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中閉包的概念並很差理解,由於牽扯到了太多全部權的概念,能夠先把全部權弄懂了,閉包也就好理解了。