闭包
- 闭包是可以捕获所在环境的匿名函数。闭包的定义:|参数列表| -> 返回类型 {代码段}
|x:u8| -> u8
{
let y = x +1;
y
};
- 闭包是一个匿名函数。
像下面合法定义,||是闭包的参数
|| 45;
|x:u8| x + 1;
|x:u8| -> u8 {
x + 1 };
- 闭包是把一个函数保存为变量,可以作为参数使用。
let y = |x:u32| x+10;
print!("{}\n",y(5))
- 可以在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算。
//如果只定义不调用,会报错
let add = |a|{
a+1};
add(1);//推导出类型完成计算
如果有两次调用,则第二次推导失败
//如果只定义不调用,会报错
let add = |a|{
a+1};
add(1);
add(2.0);
- 闭包不要求标注参数和返回值的类型
- 闭包通常很短小,只在狭小的上下文言中工作,编译器通常能推断出类型
- 闭包可以手动添加类型标注
- 可以在定义的地方直接调用
let str = (|x:String|x + &" world".to_string())("hello".to_string());
print!("{}\n",str);
- 可以从其定义的作用域捕获值。
let m = 5;
let add = |a|{
a+m};//这里捕获了当前环境外部m的值
print!("{}\n",add(10));//输出15
闭包捕获变量关联的trait定义
pub trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
pub trait FnMut<Args>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
pub trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
- Fn:如果闭包只是对捕获变量不做修改操作,闭包捕获的是&T类型,闭包按照Fn trait方式执行,闭包可以重复多次执行。
- FnMut:如果闭包对捕获变量有修改操作,闭包捕获的是&mut T类型,闭包按照FnMut trait方式执行,闭包可以重复多次执行
- FnOnce:如果闭包会消耗掉捕获的变量,变量被move进闭包,闭包按照FnOnce trait方式执行,闭包只能执行一次
7.闭包作为函数参数传递
这样写是不可以的,因为闭包不属于任何类型,本质闭包是trait
fn main()
{
let x = |y:u16| {
y+10};
add(x);
}
fn add(x : u16)
{
x(10);
}
这两种写法都可以:
fn main()
{
let x = |y|{
print!("{} \n",y)};
foo_1(x);
}
fn foo_1<F : Fn(u16)>(x: F)
{
x(5);
}
fn foo_2(x: impl Fn(u16))
{
x(10);
}
- 返回闭包
闭包属于trait,这意味着不能直接返回闭包。对于大部分需要返回trait的情况,可以使用实现了期望返回trait的具体类型来替代函数的返回值。但是这不能用于闭包,因为他们没有一个可返回的具体类型;例如不允许使用函数指针fn作为返回值类型。
下面的例子编译不能通过
fn return_closure() -> Fn(i32) -> i32
{
|x| x+1
}