快速掌握Rust闭包
闭包(Closure) : 也叫Lambda表达式或匿名函数。
不像普通函数,闭包可以对参数和返回类型进行推断,大多数时候都不需要写出来。以下定义都是合法的:
|| 42;
|x| x + 1;
|x:i32| x + 1;
|x:i32| -> i32 { x + 1 };
在上面的例子中,如果是单行语句且没有标注返回类型的时候,花括号是可选的。
- 闭包可以像任何其他对象一样绑定到某个变量,然后可以像调用函数一样调用闭包
let f = |x| x + 1;
println!("x is {}", f(5))
打印结果如下:
x is 6
也可以在定义的地方直接调用:
let r = (|x| x + 1)(2); // r == 3
- 闭包可以捕获外部的环境变量(自由变量)。
闭包捕获变量的方式分为三类:引用(&T)、可变引用(&mut T)和值(T)。捕获变量时,闭包会根据上面列出的顺序(从约束最少到约束最多),优先按引用捕获,必要时才会使用后面的捕获方式:
let x = 10;
// 闭包按引用捕获变量x,因为println!只需要引用参数
let show_x = || println!("x = {}", x);
show_x();
外部变量的引用保存在show_x对象中,对外部变量的借用持续到show_x离开作用域为止。
下面是一个捕获可变引用的例子:
let mut count = 0;
// 闭包按可变引用捕获变量count
// incr也必须是可变的,因为它持有可变引用,调用incr会改变闭包的状态
let mut incr = || { count += 1; println!("count = {}", count); };
incr();
下面的代码演示了闭包转移捕获变量所有权时的情况(捕获变量的值):
use std::mem;
// b是不可复制类型,因此按值捕获时所有权会转移
let b = Box::new(12);
let f = || {
println!("b = {}", b);
// drop函数取T类型,因此闭包会按值捕获变量b
mem::drop(b);
};
f();
调用闭包后b的所有权已经转移,无法再访问:
// 编译错误
println!("b = {}", b);
同样,因为b的所有权已经转移,无法再次调用这个闭包:
// 编译错误
f();
如果要强制按值捕获,可以在闭包前添加关键字move:
let f = move || {
println!("b = {}", b);
};
f();
同样地,调用闭包后b的所有权已经转移,无法再访问。