第十三回:锦囊妙计,Rust 闭包
闭包就是可以保存进变量、或者作为参数传递给其他函数的匿名函数。它最厉害的地方在于:它可以捕获调用者作用域中的变量。
1. 定义闭包 (写锦囊)
闭包不使用 fn,而是使用管道符 || 来包含参数。
fn main() {
let x = 1;
// 定义一个闭包,它捕获了外面的 x
let add_x = |y| y + x;
let result = add_x(5); // 5 + 1 = 6
println!("结果是:{}", result);
}
如果用普通函数,是没法直接用外面的 x 的,除非你传进去。闭包就方便多了,自带包裹!
2. 类型推断 (心有灵犀)
闭包通常不需要像函数那样写明参数类型,编译器会猜(推断)。
let example = |x| x;
let s = example("hello"); // 第一次调用,编译器推断 x 是 &str
// let n = example(5); // 报错!编译器已经认定它是处理字符串的了
3. 捕获方式 (三种境界)
闭包捕获变量有三种方式,对应 Rust 的所有权规则:
Fn:借用(只读)。FnMut:可变借用(读写)。FnOnce:获取所有权(吃干抹净,只能用一次)。
fn main() {
let mut count = 0;
// 必须加上 mut,因为闭包要修改里面的环境
let mut counter = || {
count += 1;
println!("打了 {} 下", count);
};
counter();
counter();
}
4. 强制转移 (move 关键字)
如果你想让闭包彻底拥有它捕获的变量(而不是借用),可以使用 move 关键字。这在多线程中非常重要。
fn main() {
let name = String::from("包龙星");
// 加上 move,name 的所有权就被移交给闭包了
let consume_name = move || {
println!("名字是:{}", name);
};
consume_name();
// println!("{}", name); // 报错!name 已经被闭包拿走了,回不来了!
}
这不就是个匿名函数吗?搞得神神秘秘的!
匿名函数能带私房钱(环境)吗?闭包能!这就是区别!
动手时刻:秘密加倍
定义一个闭包,捕获外部变量 factor (倍数),然后计算输入数字的倍数。
fn main() {
let factor = 10;
// 定义闭包 multiplier
// println!("5 的 {} 倍是 {}", factor, multiplier(5));
}
查看锦囊
let multiplier = |x| x * factor;