包龙星

这个案子太复杂,我要派个密探去查,但他得带着我的尚方宝剑(环境)去,这怎么办?

雷豹

给个锦囊妙计!这就是闭包 (Closure)!它不仅是一段代码,还能把周围的变量打包带走!

第十三回:锦囊妙计,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 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;
下一回:尚方宝剑(所有权) →