二十七、Rust 闭包 Closure

闭包( Closure ) 的出现其实是程序员偷懒的结果,也可以说是语言开发者为程序员送的福利。

为什么这么说呢 ?

我们来看看几个 闭包 的解释:

  • 闭包就是在一个函数内创建立即调用的另一个函数。

  • 闭包是一个匿名函数。也就是没有函数名称。

  • 闭包虽然没有函数名,但可以把整个闭包赋值一个变量,通过调用该变量来完成闭包的调用。从某些方面说,这个变量就是函数名的作用。

  • 闭包不用声明返回值,但它却可以有返回值。并且使用最后一条语句的执行结果作为返回值。闭包的返回值可以赋值给变量。

  • 闭包有时候有些地方又称之为 内联函数。这种特性使得闭包可以访问外层函数里的变量。

从上面的描述中可以看出,闭包就是函数内部的一个没有函数名的内联函数。对于那些只使用一次的函数,使用闭包是最佳的代替方案。

27.1 定义闭包的语法

在介绍如何定义闭包前,我们先来看看 如何定义一个普通的函数。

一个普通的函数的定义语法格式如下

  1. fn function_name(parameters) -> return_type {
  2. // 函数的具体逻辑
  3. }

从上面的描述中我们知道,闭包是一个没有函数名的内联函数。它的定义语法如下

  1. |parameter| {
  2. // 闭包的具体逻辑
  3. }

从语法格式上来看,闭包就是普通函数去掉 fn 关键字,去掉函数名,去掉返回值声明,并把一对小括号改成一对 竖线 ||。

闭包的参数是可选的,如果一个闭包没有参数,那么它的定义语法格式如下

  1. ||{
  2. // 闭包的具体逻辑
  3. }

闭包虽然没有名称,但我们可以将闭包赋值给一个变量,然后就可以通过调用这个变量来完成闭包的调用。

  1. let closure_function = |parameter| {
  2. // 闭包的具体逻辑
  3. }

因为调用闭包的语法实现了 Fn 特质,因此我们可以像调用普通函数那样,使用小括号 () 来调用闭包

  1. closure_function(parameter); //invoking

27.1.1 范例1:普通的闭包

下面的代码我们定义了一个闭包用于判断传递的参数是否偶数,并把闭包赋值给变量 is_even。

然后我们通过调用 is_even() 来完成闭包的调用。

  1. fn main(){
  2. let is_even = |x| {
  3. x%2==0
  4. };
  5. let no = 13;
  6. println!("{} is even ? {}",no,is_even(no));
  7. }

编译运行以上 Rust 代码,输出结果如下

  1. 13 is even ? false

27.1.2 范例2:闭包使用外部函数可以访问的变量

闭包还可以访问它所在的外部函数可以访问的所有变量。

  1. fn main(){
  2. let val = 10;
  3. // 访问外层作用域变量 val
  4. let closure2 = |x| {
  5. x + val // 内联函数访问外层作用域变量
  6. };
  7. println!("{}",closure2(2));
  8. }

编译运行以上 Rust 代码,输出结果如下

  1. 12