闭包能够从定义它的上下文环境中捕获值。

闭包的语法:

|参数列表| {
	// 略
}

闭包是不强制要求标注参数和返回值的类型的,这里有这样几个原因:

  1. 闭包不需要对外暴露
  2. 闭包只在狭小的上下文中使用,编译器能够可靠推出类型。
  3. 闭包是很简短的,如果要求标注类型非常影响编码体验。 不过如果要显式标注也是可以的。

闭包内部体只有一条表达式的话,可以省略花括号。

使用泛型参数和Fn trait来存储闭包

用一个结构体保存结果和闭包,当闭包没有运行结果的时候才运行闭包,有结果就直接复用。这种模式被称为记忆化或惰性求值。

为了将闭包存储在结构体中,需要指明闭包的类型。而每一个闭包都具有自己的匿名类型(即使两个闭包的签名一致,类型也是不同的)。因此要表达一个闭包的类型就需要使用 trait约束 了。

所有的闭包都实现了FnFnMutFnOnce trait中的一个。 定义一个存储闭包和其结果的结构体:

struct Cacher<T>
	where T: Fn(u32) -> u32
{
	calculation: T,
	value: Option<u32>,	
}

为其实现new函数以及惰性求值方法

impl <T> Cacher<T>
	where T:Fn(u32)->u32
{
	fn new(calculation: T) -> Cacher<T> {
		Cacher {
			calculation,
			value: None,
		}
	}
 
	fn value(&mut self, arg) -> u32 {
		match self.value {
			Some(v) => v,
			None => {
				let v = (self.calculation)(arg);
				self.value = Some(v);
				v
			},
		}
	}
}

捕获上下文环境

闭包可以捕获其上下文环境中的变量,例如

fn main {
	let x = 4;
	let equal_to_x = |a| {
		a == x
	}
	
}

闭包捕获值,会使用额外的空间来存储这些值以便在闭包体内使用。相当于隐式定义了一个结构体用来存储捕获的值,然后为这个结构体实现了一个方法,就是闭包函数体。

闭包捕获值与函数接收参数的3中方式是一致的:

  • 获得所有权
  • 可变借用
  • 不可变借用

这也与三种trait对应:

  1. FnOnce:消耗捕获的变量,需要获取捕获值的所有权。同一变量的所有权不能被多次获取并消耗,因此只能调用一次
  2. FnMut:从环境中可变借用值。
  3. Fn:从环境中不可变借用值。

Rust会根据闭包中使用值的方式来自动推导出它需要使用的trait。 所有的闭包都实现了FnOnce,至少可以调用一次。不需要移动捕获变量的实现了FnMut,不需要对捕获变量进行可变访问的闭包同时实现了Fn。

假如你希望强制闭包获取环境中值的所有权,那么你可以在参数列表前添加move关键字

fn main() {    
	let x = vec![1, 2, 3];    
	let equal_to_x = move |z| z == x;
}

tags: 函数式编程