鍍金池/ 教程/ Java/ 閉包的語(yǔ)法
標(biāo)準(zhǔn)輸入與輸出
消息傳遞
循環(huán)
注釋
Rust for Mac OS
幾種智能指針
Cell, RefCell
trait對(duì)象 (trait object)
rust web 開(kāi)發(fā)
Unsafe、原始指針
Macro
迭代器
函數(shù)
Borrow, BorrowMut, ToOwned
快速上手
二叉樹(shù)
編輯器
測(cè)試與評(píng)測(cè)
Deref
安裝Rust
哈希表 HashMap
原生類型
17.錯(cuò)誤處理
VS Code 安裝配置
動(dòng)態(tài)數(shù)組Vec
模式匹配
操作符和格式化字符串
Rust for Linux
函數(shù)參數(shù)
Visual Studio
vim/GVim安裝配置
閉包作為參數(shù)和返回值
安全(Safety)
Cow
生命周期( Lifetime )
閉包的實(shí)現(xiàn)
所有權(quán)(Ownership)
Atom
將Rust編譯成庫(kù)
類型、運(yùn)算符和字符串
類型系統(tǒng)中的幾個(gè)常見(jiàn) trait
特性
屬性和編譯器參數(shù)
Spacemacs
集合類型
Rust json處理
Heap & Stack
并行
標(biāo)準(zhǔn)庫(kù)示例
基本程序結(jié)構(gòu)
鏈表
trait 和 trait對(duì)象
前期準(zhǔn)備
代碼風(fēng)格
編譯器參數(shù)
基于語(yǔ)義化版本的項(xiàng)目版本聲明與管理
Rust 版本管理工具: rustup
引用&借用(References&Borrowing)
注釋與文檔
10.1 trait關(guān)鍵字
模式
調(diào)用ffi函數(shù)
unsafe
并發(fā),并行,多線程編程
AsRef 和 AsMut
Rust旅程
Rust for Windows
結(jié)構(gòu)體與枚舉
條件分支
附錄I-術(shù)語(yǔ)表
變量綁定與原生類型
Mutex 與 RwLock
泛型
裸指針
常用數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)
系統(tǒng)命令:調(diào)用grep
Into/From 及其在 String 和 &str 互轉(zhuǎn)上的應(yīng)用
共享內(nèi)存
Sublime
網(wǎng)絡(luò)模塊:W貓的回音
函數(shù)返回值
包和模塊
高階函數(shù)
函數(shù)與方法
match關(guān)鍵字
隊(duì)列
目錄操作:簡(jiǎn)單grep
語(yǔ)句和表達(dá)式
并發(fā)編程
閉包
測(cè)試
閉包的語(yǔ)法
同步
迭代器
String
Send 和 Sync
Rc 和 Arc
屬性
Emacs
優(yōu)先隊(duì)列
Prelude
cargo簡(jiǎn)介
控制流(control flow)
數(shù)組、動(dòng)態(tài)數(shù)組和字符串
FFI
模塊和包系統(tǒng)、Prelude
實(shí)戰(zhàn)篇
Rust 是一門系統(tǒng)級(jí)編程語(yǔ)言,被設(shè)計(jì)為保證內(nèi)存和線程安全,并防止段錯(cuò)誤。作為系統(tǒng)級(jí)編程語(yǔ)言,它的基本理念是 “零開(kāi)銷抽象”。理
運(yùn)算符重載
Any和反射
rust數(shù)據(jù)庫(kù)操作
輸入輸出流
復(fù)合類型
性能測(cè)試

閉包的語(yǔ)法

基本形式

閉包看起來(lái)像這樣:

let plus_one = |x: i32| x + 1;

assert_eq!(2, plus_one(1));

我們創(chuàng)建了一個(gè)綁定,plus_one,并把它賦予一個(gè)閉包。閉包的參數(shù)位于管道(|)之中,而閉包體是一個(gè)表達(dá)式,在這個(gè)例子中,x + 1。記住{}是一個(gè)表達(dá)式,所以我們也可以擁有包含多行的閉包:

let plus_two = |x| {
    let mut result: i32 = x;

    result += 1;
    result += 1;

    result
};

assert_eq!(4, plus_two(2));

你會(huì)注意到閉包的一些方面與用fn定義的常規(guī)函數(shù)有點(diǎn)不同。第一個(gè)是我們并不需要標(biāo)明閉包接收和返回參數(shù)的類型。我們可以:

let plus_one = |x: i32| -> i32 { x + 1 };

assert_eq!(2, plus_one(1));

不過(guò)我們并不需要這么寫。為什么呢?基本上,這是出于“人體工程學(xué)”的原因。因?yàn)闉槊瘮?shù)指定全部類型有助于像文檔和類型推斷,而閉包的類型則很少有文檔因?yàn)樗鼈兪悄涿模⑶也⒉粫?huì)產(chǎn)生像推斷一個(gè)命名函數(shù)的類型這樣的“遠(yuǎn)距離錯(cuò)誤”。

第二個(gè)的語(yǔ)法大同小異。我會(huì)增加空格來(lái)使它們看起來(lái)更像一點(diǎn):

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  ;

捕獲變量

之所以把它稱為“閉包”是因?yàn)樗鼈儭鞍诃h(huán)境中”(close over their environment)。這看起來(lái)像:

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

assert_eq!(10, plus_num(5));

這個(gè)閉包,plus_num,引用了它作用域中的let綁定:num。更明確的說(shuō),它借用了綁定。如果我們做一些會(huì)與這個(gè)綁定沖突的事,我們會(huì)得到一個(gè)錯(cuò)誤。比如這個(gè):

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

let y = &mut num;

錯(cuò)誤是:

error: cannot borrow `num` as mutable because it is also borrowed as immutable
    let y = &mut num;
                 ^~~
note: previous borrow of `num` occurs here due to use in closure; the immutable
  borrow prevents subsequent moves or mutable borrows of `num` until the borrow
  ends
    let plus_num = |x| x + num;
                   ^~~~~~~~~~~
note: previous borrow ends here
fn main() {
    let mut num = 5;
    let plus_num = |x| x + num;

    let y = &mut num;
}
^

一個(gè)啰嗦但有用的錯(cuò)誤信息!如它所說(shuō),我們不能取得一個(gè)num的可變借用因?yàn)殚]包已經(jīng)借用了它。如果我們讓閉包離開(kāi)作用域,我們可以:

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;

如果你的閉包需要它,Rust會(huì)取得所有權(quán)并移動(dòng)環(huán)境:

let nums = vec![1, 2, 3];

let takes_nums = || nums;

println!("{:?}", nums);

這會(huì)給我們:

note: `nums` moved into closure environment here because it has type
  `[closure(()) -> collections::vec::Vec<i32>]`, which is non-copyable
let takes_nums = || nums;
                    ^~~~~~~

Vec<T>擁有它內(nèi)容的所有權(quán),而且由于這個(gè)原因,當(dāng)我們?cè)陂]包中引用它時(shí),我們必須取得nums的所有權(quán)。這與我們傳遞nums給一個(gè)取得它所有權(quán)的函數(shù)一樣。

move閉包

我們可以使用move關(guān)鍵字強(qiáng)制使我們的閉包取得它環(huán)境的所有權(quán):

let num = 5;

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

現(xiàn)在,即便關(guān)鍵字是move,變量遵循正常的移動(dòng)語(yǔ)義。在這個(gè)例子中,5實(shí)現(xiàn)了Copy,所以owns_num取得一個(gè)5的拷貝的所有權(quán)。那么區(qū)別是什么呢?

let mut num = 5;

{
    let mut add_num = |x: i32| num += x;

    add_num(5);
}

assert_eq!(10, num);

那么在這個(gè)例子中,我們的閉包取得了一個(gè)num的可變引用,然后接著我們調(diào)用了add_num,它改變了其中的值,正如我們期望的。我們也需要將add_num聲明為mut,因?yàn)槲覀儠?huì)改變它的環(huán)境。

如果我們加上move修飾閉包,會(huì)發(fā)生些不同:

let mut num = 5;

{
    let mut add_num = move |x: i32| num += x;

    add_num(5);
}

assert_eq!(5, num);

我們只會(huì)得到5。這次我們沒(méi)有獲取到外部的num的可變借用,我們實(shí)際上是把 num move 進(jìn)了閉包。因?yàn)?num 具有 Copy 屬性,因此發(fā)生 move 之后,以前的變量生命周期并未結(jié)束,還可以繼續(xù)在 assert_eq! 中使用。我們打印的變量和閉包內(nèi)的變量是獨(dú)立的兩個(gè)變量。如果我們捕獲的環(huán)境變量不是 Copy 的,那么外部環(huán)境變量被 move 進(jìn)閉包后, 它就不能繼續(xù)在原先的函數(shù)中使用了,只能在閉包內(nèi)使用。

不過(guò)在我們討論獲取或返回閉包之前,我們應(yīng)該更多的了解一下閉包實(shí)現(xiàn)的方法。作為一個(gè)系統(tǒng)語(yǔ)言,Rust給予你了大量的控制你代碼的能力,而閉包也是一樣。

這部分引用自The Rust Programming Language中文版