rust中的闭包

Rust 的 闭包(closures)是可以保存进变量或作为参数传递给其他函数的匿名函数。可以在一个地方创建闭包,然后在不同的上下文中执行闭包运算。不同于函数,闭包允许捕获调用者作用域中的值。我们将展示闭包的这些功能如何复用代码和自定义行为。

一、闭包的语法

闭包基本语法

可以从示例上看出,闭包的语法跟函数很像。三个部分如下:

|参数1, 参数2, ...| 

->返回值类型

{执行体}

执行结果如下图:

执行结果

可以发现没有报错正常通过了,即完成了我们预期的功能。

闭包的常见省略形式有如下三种:

三种常见省略

而当闭包在捕获上文的自由变量时,管道符里的参数也可以省略:

捕获外部环境变量时的省略

如上图展示的代码,也是能正常通过编译的。


二、闭包的二次调用错误

示例代码如下:

示例

可以看到首先我们有一个闭包的定义,当我们在调用闭包的时候,先传进去了string类型的参数,之后又调用闭包传进了int32类型的参数,此时编译就会出错了。

编译出错

编译会出错的原因如下。第一次使用 String 值调用 example_closure 时,编译器推断 x 和此闭包返回值的类型为String 。接着这些类型被锁定进闭包 example_closure 中,如果尝试对同一闭包使用不同类型则会得到类型错误。

三、闭包捕获环境中的值

闭包可以作为内联匿名函数来使用,不过闭包还有另一个函数所没有的功能:他们可以捕获其环境并访问其被定义的作用域的变量。

有示例代码如下所示:

闭包捕获环境

这里,即便 x 并不是 equal_to_x 的一个参数, equal_to_x 闭包也被允许使用变量 x ,因为它与 equal_to_x 定义于相同的作用域。

这就是闭包的神奇之处,函数是不能做到这一点的。

当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用。这会使用内存并产生额外的开销,在更一般的场景中,当我们不需要闭包来捕获环境时,我们不希望产生这些开销。因为函数从未允许捕获环境,定义和使用函数也就从不会有这些额外开销。闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 Fn trait:

文档中描述的三种trait

看完了rust文档中的描述,来做个简单总结:

Fn trait,表示闭包以不可变引用的方式来捕获环境中的自由变量,同时也表示该闭包没有改变环境的能力,并且可以多次调用。对应&self

FnMut trait,表示闭包以可变引用的方式来捕获环境中的自由变量,同时意味着该闭包有改变环境的能力,也可以多次调用。对应&mut self

FnOnce trait,表示闭包通过转移所有权来捕获环境中的自由变量,同时意味着该闭包没有改变环境的能力,只能调用一次,因为该闭包会消耗自身。对应self

1、Fn闭包

有如下示例代码:

示例

编译运行后,结果如下:

示例输出

可以看到,闭包对环境中a的捕获,是以一种类似函数中不可变引用传参的方式进行的,闭包没有取得a的所有权,所以在闭包调用完之后,在println!中还可以正常使用a。

2、FnMut闭包

有如下示例代码:

示例

当闭包中会修改外部环境的自由变量值时,函数闭包fn_mut_closure也得加上mut关键词。当闭包执行时,它默认以可变引用的形式传入闭包,闭包没有获取其所有权,所以闭包执行结束后,println!可以正常输出a的值。输出如下:

输出

输出中可以看出,环境中的自由变量a的值确实被改变了。

3、FnOnce闭包

有示例如下:

示例

这个示例中,闭包call_me中发生了所有权的移交(move语义),Box指针所指向的堆内存的所有权从a移交给了c,而随着c离开包后生命周期的结束,该堆内存也被释放掉。

环境中的自由变量a在被闭包执行一次之后,就失效了。

报错

这种情况下二次调用肯定就发生报错了。

4、闭包强行获得环境中自由变量的所有权

如果你希望强制闭包获取其使用的环境值的所有权,可以在参数列表前使用 move 关键字。这个技巧在将闭包传递给新线程以便将数据移动到新线程中时最为实用。

如我们在Fn闭包中展示的代码,如果我们在参数列表前加上了move关键字,如下图:

move关键字会获取所有权

闭包获得了a的所有权,因此编译会出错:

编译出错

编译出错的信息一目了然,就是因为move强行让所有权从a移到了闭包上,原来的a也就失效了,没有办法被println!打印出来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容